diff --git a/components/connectivity/iot-hub-device-c-sdk/.clang-format b/components/connectivity/iot-hub-device-c-sdk/.clang-format
new file mode 100644
index 00000000..34114f0e
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/.clang-format
@@ -0,0 +1,10 @@
+BasedOnStyle: Google
+BreakBeforeBraces: Linux
+AlignConsecutiveDeclarations: true
+AlignConsecutiveMacros: true
+AlignConsecutiveAssignments: true
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: false
+SortIncludes: false
+IndentWidth: 4
+ColumnLimit: 120
\ No newline at end of file
diff --git a/components/connectivity/iot-hub-device-c-sdk/.code.yml b/components/connectivity/iot-hub-device-c-sdk/.code.yml
new file mode 100644
index 00000000..c77d7b4a
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/.code.yml
@@ -0,0 +1,11 @@
+source:
+ auto_generate_source:
+ # 自动生成代码文件的正则表达式,若无统一标识格式,可以指定具体目录,样例可参考test_source举例
+ filepath_regex: [
+ ".*/build/.*",
+ ".*/output/.*",
+ ]
+ third_party_source:
+ filepath_regex: [
+ ".*/3rd/mbedtls/mbedtls/.*",
+ ]
\ No newline at end of file
diff --git a/components/connectivity/iot-hub-device-c-sdk/.gitignore b/components/connectivity/iot-hub-device-c-sdk/.gitignore
new file mode 100644
index 00000000..6d9a1129
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/.gitignore
@@ -0,0 +1,7 @@
+/.vscode/
+/build/
+/output/
+/tmp/
+build.yml
+app_ota_fw.bin
+break_point.dat
diff --git a/components/connectivity/iot-hub-device-c-sdk/.gitmodules b/components/connectivity/iot-hub-device-c-sdk/.gitmodules
new file mode 100644
index 00000000..3d4ca13d
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/.gitmodules
@@ -0,0 +1,4 @@
+[submodule "3rd/mbedtls/mbedtls"]
+ path = 3rd/mbedtls/mbedtls
+ url = https://github.com/ARMmbed/mbedtls.git
+ branch = mbedtls-2.16
diff --git a/components/connectivity/iot-hub-device-c-sdk/3rd/mbedtls/CMakeLists.txt b/components/connectivity/iot-hub-device-c-sdk/3rd/mbedtls/CMakeLists.txt
new file mode 100644
index 00000000..446e846b
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/3rd/mbedtls/CMakeLists.txt
@@ -0,0 +1,18 @@
+option(USE_STATIC_MBEDTLS_LIBRARY "Build mbed TLS static library." ON)
+option(USE_SHARED_MBEDTLS_LIBRARY "Build mbed TLS shared library." OFF)
+option(LINK_WITH_PTHREAD "Explicitly link mbed TLS library to pthread." OFF)
+
+file(GLOB src_mbedtls
+ ${CMAKE_CURRENT_SOURCE_DIR}/mbedtls/library/*.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/port/src/*.c
+)
+
+if(${CONFIG_EXTRACT_SRC} STREQUAL "ON")
+ file(GLOB inc_mbedtls ${CMAKE_CURRENT_SOURCE_DIR}/mbedtls/include/mbedtls/*.h)
+ file(GLOB inc_port ${CMAKE_CURRENT_SOURCE_DIR}/port/inc/*h)
+ file(COPY ${src_mbedtls} DESTINATION ${PROJECT_SOURCE_DIR}/output/sdk/src/3rd/mbedtls)
+ file(COPY ${inc_mbedtls} DESTINATION ${PROJECT_SOURCE_DIR}/output/sdk/inc/3rd/mbedtls)
+ file(COPY ${inc_port} DESTINATION ${PROJECT_SOURCE_DIR}/output/sdk/inc/3rd)
+endif()
+
+add_library(mbedtls STATIC ${src_mbedtls})
diff --git a/components/connectivity/iot-hub-device-c-sdk/3rd/mbedtls/port/inc/qcloud_iot_tls_client.h b/components/connectivity/iot-hub-device-c-sdk/3rd/mbedtls/port/inc/qcloud_iot_tls_client.h
new file mode 100644
index 00000000..cb687e01
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/3rd/mbedtls/port/inc/qcloud_iot_tls_client.h
@@ -0,0 +1,119 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_tls_client.h
+ * @brief header file for tls client
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-07-12
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-07-12 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_3RD_MBEDTLS_PORT_INC_QCLOUD_IOT_TLS_CLIENT_H_
+#define IOT_HUB_DEVICE_C_SDK_3RD_MBEDTLS_PORT_INC_QCLOUD_IOT_TLS_CLIENT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "qcloud_iot_platform.h"
+
+#ifndef MAX_SIZE_OF_CLIENT_ID
+#define MAX_SIZE_OF_CLIENT_ID (80)
+#endif
+
+/**
+ * @brief Define structure for TLS connection parameters
+ *
+ */
+typedef struct {
+ const char *ca_crt;
+ uint16_t ca_crt_len;
+
+#ifdef AUTH_MODE_CERT
+ /**
+ * Device with certificate
+ */
+ const char *cert_file; // public certificate file
+ const char *key_file; // pravite certificate file
+#else
+ /**
+ * Device with PSK
+ */
+ const char *psk; // PSK string
+ const char *psk_id; // PSK ID
+#endif
+
+ size_t psk_length; // PSK length
+
+ unsigned int timeout_ms; // SSL handshake timeout in millisecond
+
+} SSLConnectParams;
+
+typedef SSLConnectParams TLSConnectParams;
+
+/**
+ * @brief Tls setup and sharkhand
+ *
+ * @param[in] connect_params connect params of tls
+ * @param[in] host server host
+ * @param[in] port server port
+ * @return tls handle, 0 for fail
+ */
+uintptr_t qcloud_iot_tls_client_connect(const TLSConnectParams *connect_params, const char *host, const char *port);
+
+/**
+ * @brief Disconect and free
+ *
+ * @param[in,out] handle tls handle
+ */
+void qcloud_iot_tls_client_disconnect(uintptr_t handle);
+
+/**
+ * @brief Write msg with tls
+ *
+ * @param[in,out] handle tls handle
+ * @param[in] msg msg to write
+ * @param[in] total_len number of bytes to write
+ * @param[in] timeout_ms timeout millsecond
+ * @param[out] written_len number of bytes writtern
+ * @return @see IotReturnCode
+ */
+int qcloud_iot_tls_client_write(uintptr_t handle, unsigned char *msg, size_t total_len, uint32_t timeout_ms,
+ size_t *written_len);
+
+/**
+ * @brief Read msg with tls
+ *
+ * @param[in,out] handle tls handle
+ * @param[out] msg msg buffer
+ * @param[in] total_len buffer len
+ * @param[in] timeout_ms timeout millsecond
+ * @param[out] read_len number of bytes read
+ * @return @see IotReturnCode
+ */
+int qcloud_iot_tls_client_read(uintptr_t handle, unsigned char *msg, size_t total_len, uint32_t timeout_ms,
+ size_t *read_len);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_3RD_MBEDTLS_PORT_INC_QCLOUD_IOT_TLS_CLIENT_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/3rd/mbedtls/port/inc/qcloud_iot_tls_psk_config.h b/components/connectivity/iot-hub-device-c-sdk/3rd/mbedtls/port/inc/qcloud_iot_tls_psk_config.h
new file mode 100644
index 00000000..63950bab
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/3rd/mbedtls/port/inc/qcloud_iot_tls_psk_config.h
@@ -0,0 +1,81 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_tls_psk_config.h
+ * @brief set config for tls psk
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-07-12
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-07-12 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_3RD_MBEDTLS_PORT_INC_QCLOUD_IOT_TLS_PSK_CONFIG_H
+#define IOT_HUB_DEVICE_C_SDK_3RD_MBEDTLS_PORT_INC_QCLOUD_IOT_TLS_PSK_CONFIG_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* System support */
+#define MBEDTLS_HAVE_ASM
+#define MBEDTLS_HAVE_TIME
+#define MBEDTLS_TIMING_ALT
+// #define MBEDTLS_ENTROPY_HARDWARE_ALT
+// #define MBEDTLS_NO_PLATFORM_ENTROPY
+
+/* mbed TLS feature support */
+#define MBEDTLS_CIPHER_MODE_CBC
+#define MBEDTLS_CIPHER_PADDING_PKCS7
+#define MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS
+#define MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN
+#define MBEDTLS_CIPHER_PADDING_ZEROS
+#define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
+
+/* mbed TLS modules */
+#define MBEDTLS_AES_C
+#define MBEDTLS_MD_C
+#define MBEDTLS_MD5_C
+#define MBEDTLS_PLATFORM_C
+#define MBEDTLS_CIPHER_C
+#define MBEDTLS_SHA1_C
+#define MBEDTLS_SHA256_C
+#define MBEDTLS_BASE64_C
+#define MBEDTLS_CTR_DRBG_C
+#define MBEDTLS_ENTROPY_C
+
+#define MBEDTLS_SSL_PROTO_TLS1_2
+#define MBEDTLS_SSL_ENCRYPT_THEN_MAC
+#define MBEDTLS_SSL_EXTENDED_MASTER_SECRET
+#define MBEDTLS_SSL_SESSION_TICKETS
+#define MBEDTLS_SSL_CLI_C
+#define MBEDTLS_SSL_TLS_C
+#define MBEDTLS_SSL_MAX_CONTENT_LEN 3584
+#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA, MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA
+
+#define MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_KEY_EXCHANGE
+
+/* For testing with compat.sh */
+#include "mbedtls/check_config.h"
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_3RD_MBEDTLS_PORT_INC_QCLOUD_IOT_TLS_PSK_CONFIG_H
diff --git a/components/connectivity/iot-hub-device-c-sdk/3rd/mbedtls/port/src/qcloud_iot_net_socket.c b/components/connectivity/iot-hub-device-c-sdk/3rd/mbedtls/port/src/qcloud_iot_net_socket.c
new file mode 100644
index 00000000..dc57ee3d
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/3rd/mbedtls/port/src/qcloud_iot_net_socket.c
@@ -0,0 +1,155 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_net_socket.c
+ * @brief implements mbedtls net socket api for tls client
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-07-09
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-07-09 1.0 fancyxu first commit
+ *
+ */
+
+#include "qcloud_iot_platform.h"
+#include "mbedtls/net_sockets.h"
+
+/**
+ * @brief Convert return code from IotReturnCode to mbedtls error code
+ *
+ * @param[in] iot_return_code @see IotReturnCode
+ * @return mbedtls error code
+ */
+static int _return_code_convert(int iot_return_code)
+{
+ switch (iot_return_code) {
+ case QCLOUD_ERR_TCP_UNKNOWN_HOST:
+ return MBEDTLS_ERR_NET_UNKNOWN_HOST;
+ case QCLOUD_ERR_TCP_SOCKET_FAILED:
+ return MBEDTLS_ERR_NET_SOCKET_FAILED;
+ case QCLOUD_ERR_TCP_CONNECT:
+ return MBEDTLS_ERR_NET_CONNECT_FAILED;
+ case QCLOUD_ERR_TCP_WRITE_TIMEOUT:
+ return MBEDTLS_ERR_SSL_WANT_WRITE;
+ case QCLOUD_ERR_TCP_WRITE_FAIL:
+ return MBEDTLS_ERR_NET_SEND_FAILED;
+ case QCLOUD_ERR_TCP_PEER_SHUTDOWN:
+ return MBEDTLS_ERR_NET_CONN_RESET;
+ case QCLOUD_ERR_TCP_READ_TIMEOUT:
+ return MBEDTLS_ERR_SSL_TIMEOUT;
+ case QCLOUD_ERR_TCP_NOTHING_TO_READ:
+ return MBEDTLS_ERR_SSL_WANT_READ;
+ case QCLOUD_ERR_TCP_READ_FAIL:
+ return MBEDTLS_ERR_NET_RECV_FAILED;
+ default:
+ return -1;
+ }
+}
+
+/**
+ * @brief Init net context
+ *
+ * @param[in,out] ctx mbedtls net context handle
+ */
+void mbedtls_net_init(mbedtls_net_context *ctx)
+{
+ ctx->fd = -1;
+}
+
+/**
+ * @brief Initiate a TCP connection with host:port and the given protocol
+ *
+ * @param[in,out] ctx mbedtls net context handle
+ * @param[in] host host to connect
+ * @param[in] port port to connect
+ * @param[in] proto no use for always tcp
+ * @return fd > 0 for success, others for mbedtls error code
+ */
+int mbedtls_net_connect(mbedtls_net_context *ctx, const char *host, const char *port, int proto)
+{
+ ctx->fd = HAL_TCP_Connect(host, port);
+ return ctx->fd < 0 ? _return_code_convert(ctx->fd) : ctx->fd;
+}
+
+/**
+ * @brief Read at most 'len' characters
+ *
+ * @param[in] ctx mbedtls net context handle
+ * @param[out] buf data buffer
+ * @param[in] len data buffer len
+ * @return > 0 for data received bytes, others for mbedtls error code
+ */
+int mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len)
+{
+ int rc;
+ size_t rlen;
+ int fd = ((mbedtls_net_context *)ctx)->fd;
+
+ rc = HAL_TCP_Read(fd, buf, len, INT_MAX, &rlen);
+
+ return rc ? _return_code_convert(rc) : rlen;
+}
+
+/**
+ * @brief Read at most 'len' characters, blocking for at most 'timeout' ms
+ *
+ * @param[in] ctx mbedtls net context handle
+ * @param[out] buf data buffer
+ * @param[in] len data buffer len
+ * @param[in] timeout read timeout
+ * @return > 0 for data received bytes, others for mbedtls error code
+ */
+int mbedtls_net_recv_timeout(void *ctx, unsigned char *buf, size_t len, uint32_t timeout)
+{
+ int rc;
+ size_t rlen;
+ int fd = ((mbedtls_net_context *)ctx)->fd;
+
+ rc = HAL_TCP_Read(fd, buf, len, timeout, &rlen);
+
+ return rc ? _return_code_convert(rc) : rlen;
+}
+
+/**
+ * @brief Write at most 'len' characters
+ *
+ * @param[in] ctx mbedtls net context handle
+ * @param[in] buf data buffer
+ * @param[in] len data buffer len
+ * @return > 0 for data writtern bytes, others for mbedtls error code
+ */
+int mbedtls_net_send(void *ctx, const unsigned char *buf, size_t len)
+{
+ int rc;
+ size_t wlen;
+ int fd = ((mbedtls_net_context *)ctx)->fd;
+
+ rc = HAL_TCP_Write(fd, buf, len, INT_MAX, &wlen);
+
+ return rc ? _return_code_convert(rc) : wlen;
+}
+
+/**
+ * @brief Gracefully close the connection
+ *
+ * @param ctx mbedtls net context handle
+ */
+void mbedtls_net_free(mbedtls_net_context *ctx)
+{
+ HAL_TCP_Disconnect(ctx->fd);
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/3rd/mbedtls/port/src/qcloud_iot_tls_client.c b/components/connectivity/iot-hub-device-c-sdk/3rd/mbedtls/port/src/qcloud_iot_tls_client.c
new file mode 100644
index 00000000..7a5628ac
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/3rd/mbedtls/port/src/qcloud_iot_tls_client.c
@@ -0,0 +1,395 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_tls_client.c
+ * @brief implements tls client with mbedtls
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-07-12
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-07-12 1.0 fancyxu first commit
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "qcloud_iot_tls_client.h"
+
+#include
+#include
+#include
+
+#include "mbedtls/ssl.h"
+#include "mbedtls/entropy.h"
+#include "mbedtls/net_sockets.h"
+#include "mbedtls/ctr_drbg.h"
+#include "mbedtls/error.h"
+#include "mbedtls/debug.h"
+
+#ifdef AUTH_MODE_KEY
+/**
+ * @brief Only tls psk is supportted when using psk
+ *
+ */
+static const int ciphersuites[] = {MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA, MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA, 0};
+#endif
+
+/**
+ * @brief Data structure for mbedtls SSL connection
+ *
+ */
+typedef struct {
+ mbedtls_net_context socket_fd;
+ mbedtls_entropy_context entropy;
+ mbedtls_ctr_drbg_context ctr_drbg;
+ mbedtls_ssl_context ssl;
+ mbedtls_ssl_config ssl_conf;
+#ifdef AUTH_MODE_CERT
+ mbedtls_x509_crt ca_cert;
+ mbedtls_x509_crt client_cert;
+#endif
+ mbedtls_pk_context private_key;
+} TLSHandle;
+
+#ifdef MBEDTLS_DEBUG_C
+#define DEBUG_LEVEL 0
+static void _ssl_debug(void *ctx, int level, const char *file, int line, const char *str)
+{
+ Log_i("[mbedTLS]:[%s]:[%d]: %s\r\n", STRING_PTR_PRINT_SANITY_CHECK(file), line, STRING_PTR_PRINT_SANITY_CHECK(str));
+}
+#endif
+
+/**
+ * @brief mbedtls SSL client init
+ *
+ * 1. call a series of mbedtls init functions
+ * 2. init and set seed for random functions
+ * 3. load CA file, cert files or PSK
+ *
+ * @param[in,out] tls_handle mbedtls TLS handle
+ * @param[in] connect_params device info for TLS connection
+ * @return @see IotReturnCode
+ */
+static int _mbedtls_tls_client_init(TLSHandle *tls_handle, const TLSConnectParams *connect_params)
+{
+ int rc;
+
+ mbedtls_net_init(&tls_handle->socket_fd);
+ mbedtls_ssl_init(&tls_handle->ssl);
+ mbedtls_ssl_config_init(&tls_handle->ssl_conf);
+ mbedtls_ctr_drbg_init(&tls_handle->ctr_drbg);
+ mbedtls_entropy_init(&tls_handle->entropy);
+#ifdef AUTH_MODE_CERT
+ mbedtls_x509_crt_init(&tls_handle->ca_cert);
+ mbedtls_x509_crt_init(&tls_handle->client_cert);
+ mbedtls_pk_init(&tls_handle->private_key);
+#endif
+#ifdef MBEDTLS_DEBUG_C
+ mbedtls_debug_set_threshold(DEBUG_LEVEL);
+ mbedtls_ssl_conf_dbg(&tls_handle->ssl_conf, _ssl_debug, NULL);
+#endif
+
+ rc = mbedtls_ctr_drbg_seed(&tls_handle->ctr_drbg, mbedtls_entropy_func, &tls_handle->entropy, NULL, 0);
+ if (rc) {
+ Log_e("mbedtls_ctr_drbg_seed failed returned 0x%04x", -rc);
+ return QCLOUD_ERR_SSL_INIT;
+ }
+
+#ifdef AUTH_MODE_CERT
+ if (!connect_params->cert_file || !connect_params->key_file || !connect_params->ca_crt) {
+ Log_d("cert_file/key_file/ca is empty!|cert_file=%s|key_file=%s|ca=%s",
+ STRING_PTR_PRINT_SANITY_CHECK(connect_params->cert_file),
+ STRING_PTR_PRINT_SANITY_CHECK(connect_params->key_file),
+ STRING_PTR_PRINT_SANITY_CHECK(connect_params->ca_crt));
+ return QCLOUD_ERR_SSL_CERT;
+ }
+
+ rc = mbedtls_x509_crt_parse(&tls_handle->ca_cert, (const unsigned char *)connect_params->ca_crt,
+ (connect_params->ca_crt_len + 1));
+ if (rc) {
+ Log_e("parse ca crt failed returned 0x%04x", -rc);
+ return QCLOUD_ERR_SSL_CERT;
+ }
+
+ rc = mbedtls_x509_crt_parse_file(&tls_handle->client_cert, connect_params->cert_file);
+ if (rc) {
+ Log_e("load client cert file failed returned 0x%04x", -rc);
+ return QCLOUD_ERR_SSL_CERT;
+ }
+
+ rc = mbedtls_pk_parse_keyfile(&tls_handle->private_key, connect_params->key_file, "");
+ if (rc) {
+ Log_e("load client key file failed returned 0x%04x", -rc);
+ return QCLOUD_ERR_SSL_CERT;
+ }
+#else
+ if (!connect_params->psk || !connect_params->psk_id) {
+ Log_d("psk/psk_id is empty!");
+ return QCLOUD_ERR_SSL_INIT;
+ }
+
+ rc = mbedtls_ssl_conf_psk(&tls_handle->ssl_conf, (unsigned char *)connect_params->psk, connect_params->psk_length,
+ (const unsigned char *)connect_params->psk_id, strlen(connect_params->psk_id));
+ if (rc) {
+ Log_e("mbedtls_ssl_conf_psk fail 0x%04x", -rc);
+ return rc;
+ }
+#endif
+ return QCLOUD_RET_SUCCESS;
+}
+
+/**
+ * @brief Free memory/resources allocated by mbedtls
+ *
+ * @param[in,out] tls_handle @see TLSHandle
+ */
+static void _mbedtls_tls_client_free(TLSHandle *tls_handle)
+{
+ mbedtls_net_free(&(tls_handle->socket_fd));
+#ifdef AUTH_MODE_CERT
+ mbedtls_x509_crt_free(&tls_handle->client_cert);
+ mbedtls_x509_crt_free(&tls_handle->ca_cert);
+ mbedtls_pk_free(&tls_handle->private_key);
+#endif
+ mbedtls_ssl_free(&tls_handle->ssl);
+ mbedtls_ssl_config_free(&tls_handle->ssl_conf);
+ mbedtls_ctr_drbg_free(&tls_handle->ctr_drbg);
+ mbedtls_entropy_free(&tls_handle->entropy);
+ HAL_Free(tls_handle);
+}
+
+#ifdef AUTH_MODE_CERT
+/**
+ * @brief verify server certificate
+ *
+ * mbedtls has provided similar function mbedtls_x509_crt_verify_with_profile
+ */
+int _mbedtls_tls_client_certificate_verify(void *hostname, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
+{
+ return *flags;
+}
+#endif
+
+/**
+ * @brief Tls setup and sharkhand
+ *
+ * @param[in] connect_params connect params of tls
+ * @param[in] host server host
+ * @param[in] port server port
+ * @return tls handle, 0 for fail
+ */
+uintptr_t qcloud_iot_tls_client_connect(const TLSConnectParams *connect_params, const char *host, const char *port)
+{
+ int rc = 0;
+
+ TLSHandle *tls_handle = (TLSHandle *)HAL_Malloc(sizeof(TLSHandle));
+ if (!tls_handle) {
+ return 0;
+ }
+
+ rc = _mbedtls_tls_client_init(tls_handle, connect_params);
+ if (rc) {
+ goto error;
+ }
+
+ Log_d("Setting up the SSL/TLS structure...");
+ rc = mbedtls_ssl_config_defaults(&tls_handle->ssl_conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM,
+ MBEDTLS_SSL_PRESET_DEFAULT);
+ if (rc) {
+ Log_e("mbedtls_ssl_config_defaults failed returned 0x%04x", -rc);
+ goto error;
+ }
+
+ mbedtls_ssl_conf_rng(&tls_handle->ssl_conf, mbedtls_ctr_drbg_random, &tls_handle->ctr_drbg);
+#ifdef AUTH_MODE_CERT
+ mbedtls_ssl_conf_verify(&(tls_handle->ssl_conf), _mbedtls_tls_client_certificate_verify, (void *)host);
+ mbedtls_ssl_conf_authmode(&(tls_handle->ssl_conf), MBEDTLS_SSL_VERIFY_REQUIRED);
+ mbedtls_ssl_conf_ca_chain(&tls_handle->ssl_conf, &tls_handle->ca_cert, NULL);
+ rc = mbedtls_ssl_conf_own_cert(&tls_handle->ssl_conf, &tls_handle->client_cert, &tls_handle->private_key);
+ if (rc) {
+ Log_e("mbedtls_ssl_conf_own_cert failed returned 0x%04x", -rc);
+ goto error;
+ }
+#endif
+ mbedtls_ssl_conf_read_timeout(&tls_handle->ssl_conf, connect_params->timeout_ms);
+ rc = mbedtls_ssl_setup(&tls_handle->ssl, &tls_handle->ssl_conf);
+ if (rc) {
+ Log_e("mbedtls_ssl_setup failed returned 0x%04x", -rc);
+ goto error;
+ }
+#ifdef AUTH_MODE_CERT
+ // Set the hostname to check against the received server certificate and sni
+ rc = mbedtls_ssl_set_hostname(&tls_handle->ssl, host);
+ if (rc) {
+ Log_e("mbedtls_ssl_set_hostname failed returned 0x%04x", -rc);
+ goto error;
+ }
+#else
+ // ciphersuites selection for PSK device
+ if (connect_params->psk) {
+ mbedtls_ssl_conf_ciphersuites(&(tls_handle->ssl_conf), ciphersuites);
+ }
+#endif
+ mbedtls_ssl_set_bio(&tls_handle->ssl, &tls_handle->socket_fd, mbedtls_net_send, mbedtls_net_recv,
+ mbedtls_net_recv_timeout);
+ Log_d("Performing the SSL/TLS handshake...");
+ Log_d("Connecting to /%s/%s...", STRING_PTR_PRINT_SANITY_CHECK(host), STRING_PTR_PRINT_SANITY_CHECK(port));
+ rc = mbedtls_net_connect(&tls_handle->socket_fd, host, port, MBEDTLS_NET_PROTO_TCP);
+ if (rc < 0) {
+ goto error;
+ }
+
+ do {
+ rc = mbedtls_ssl_handshake(&tls_handle->ssl);
+ if (rc && rc != MBEDTLS_ERR_SSL_WANT_READ && rc != MBEDTLS_ERR_SSL_WANT_WRITE) {
+ Log_e("mbedtls_ssl_handshake failed returned 0x%04x", -rc);
+
+#ifdef AUTH_MODE_CERT
+ if (rc == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) {
+ Log_e("Unable to verify the server's certificate");
+ }
+#endif
+ goto error;
+ }
+ } while (rc);
+
+ rc = mbedtls_ssl_get_verify_result(&(tls_handle->ssl));
+ if (rc) {
+ Log_e("mbedtls_ssl_get_verify_result failed returned 0x%04x", -rc);
+ goto error;
+ }
+
+ mbedtls_ssl_conf_read_timeout(&tls_handle->ssl_conf, 200);
+
+ Log_d("connected with /%s/%s...", STRING_PTR_PRINT_SANITY_CHECK(host), port);
+ return (uintptr_t)tls_handle;
+
+error:
+ _mbedtls_tls_client_free(tls_handle);
+ return 0;
+}
+
+/**
+ * @brief Disconect and free
+ *
+ * @param[in,out] handle tls handle
+ */
+void qcloud_iot_tls_client_disconnect(uintptr_t handle)
+{
+ int rc = 0;
+
+ TLSHandle *tls_handle = (TLSHandle *)handle;
+ if (!tls_handle) {
+ Log_d("handle is NULL");
+ return;
+ }
+
+ do {
+ rc = mbedtls_ssl_close_notify(&tls_handle->ssl);
+ } while (rc == MBEDTLS_ERR_SSL_WANT_READ || rc == MBEDTLS_ERR_SSL_WANT_WRITE);
+ _mbedtls_tls_client_free(tls_handle);
+}
+
+/**
+ * @brief Write msg with tls
+ *
+ * @param[in,out] handle tls handle
+ * @param[in] msg msg to write
+ * @param[in] total_len number of bytes to write
+ * @param[in] timeout_ms timeout millsecond
+ * @param[out] written_len number of bytes writtern
+ * @return @see IotReturnCode
+ */
+int qcloud_iot_tls_client_write(uintptr_t handle, unsigned char *msg, size_t total_len, uint32_t timeout_ms,
+ size_t *written_len)
+{
+ QcloudIotTimer timer;
+ size_t written_so_far;
+ int write_rc = 0;
+
+ TLSHandle *tls_handle = (TLSHandle *)handle;
+ IOT_Timer_CountdownMs(&timer, (unsigned int)timeout_ms);
+
+ for (written_so_far = 0; written_so_far < total_len && !IOT_Timer_Expired(&timer); written_so_far += write_rc) {
+ do {
+ write_rc = mbedtls_ssl_write(&tls_handle->ssl, msg + written_so_far, total_len - written_so_far);
+ if (write_rc < 0 && write_rc != MBEDTLS_ERR_SSL_WANT_READ && write_rc != MBEDTLS_ERR_SSL_WANT_WRITE) {
+ Log_e("HAL_TLS_write failed 0x%04x", -write_rc);
+ return QCLOUD_ERR_SSL_WRITE;
+ }
+
+ if (IOT_Timer_Expired(&timer)) {
+ break;
+ }
+ } while (write_rc <= 0);
+ }
+
+ *written_len = written_so_far;
+ if (IOT_Timer_Expired(&timer) && written_so_far != total_len) {
+ return QCLOUD_ERR_SSL_WRITE_TIMEOUT;
+ }
+ return QCLOUD_RET_SUCCESS;
+}
+
+/**
+ * @brief Read msg with tls
+ *
+ * @param[in,out] handle tls handle
+ * @param[out] msg msg buffer
+ * @param[in] total_len buffer len
+ * @param[in] timeout_ms timeout millsecond
+ * @param[out] read_len number of bytes read
+ * @return @see IotReturnCode
+ */
+int qcloud_iot_tls_client_read(uintptr_t handle, unsigned char *msg, size_t total_len, uint32_t timeout_ms,
+ size_t *read_len)
+{
+ QcloudIotTimer timer;
+ int read_rc;
+
+ TLSHandle *tls_handle = (TLSHandle *)handle;
+ IOT_Timer_CountdownMs(&timer, timeout_ms);
+
+ *read_len = 0;
+
+ do {
+ read_rc = mbedtls_ssl_read(&tls_handle->ssl, msg + *read_len, total_len - *read_len);
+ if (read_rc <= 0 && read_rc != MBEDTLS_ERR_SSL_WANT_WRITE && read_rc != MBEDTLS_ERR_SSL_WANT_READ &&
+ read_rc != MBEDTLS_ERR_SSL_TIMEOUT) {
+ Log_e("cloud_iot_network_tls_read failed: 0x%04x", -read_rc);
+ return QCLOUD_ERR_SSL_READ;
+ }
+ *read_len += read_rc > 0 ? read_rc : 0;
+
+ if (IOT_Timer_Expired(&timer)) {
+ break;
+ }
+ } while (*read_len < total_len);
+
+ if (*read_len > 0) {
+ return QCLOUD_RET_SUCCESS;
+ }
+
+ return *read_len == 0 ? QCLOUD_ERR_SSL_NOTHING_TO_READ : QCLOUD_ERR_SSL_READ_TIMEOUT;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/components/connectivity/iot-hub-device-c-sdk/CMakeLists.txt b/components/connectivity/iot-hub-device-c-sdk/CMakeLists.txt
new file mode 100644
index 00000000..0b76ca63
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/CMakeLists.txt
@@ -0,0 +1,14 @@
+# 项目信息
+project(QCloud_IoT_SDK C CXX)
+
+# CMake 最低版本号要求
+cmake_minimum_required(VERSION 3.5)
+
+# 设置根目录地址
+set(IOT_SDK_SOURCE_DIR ${PROJECT_SOURCE_DIR})
+
+# 编译工具链
+include(${PROJECT_SOURCE_DIR}/config/toolchains/tencentos_tiny_none.cmake)
+
+# 工程配置
+include(${PROJECT_SOURCE_DIR}/config/settings/iot_explorer_at_module.cmake)
diff --git a/components/connectivity/iot-hub-device-c-sdk/Doxyfile b/components/connectivity/iot-hub-device-c-sdk/Doxyfile
new file mode 100644
index 00000000..a9663e7b
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/Doxyfile
@@ -0,0 +1,2495 @@
+# Doxyfile 1.8.13
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = "qcloud-iot-hub-c-sdk"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = output
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 0.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS = 0
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDEBODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDEBODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if ... \endif and \cond
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT =
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
+
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.idl \
+ *.ddl \
+ *.odl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.cs \
+ *.d \
+ *.php \
+ *.php4 \
+ *.php5 \
+ *.phtml \
+ *.inc \
+ *.m \
+ *.markdown \
+ *.md \
+ *.mm \
+ *.dox \
+ *.py \
+ *.pyw \
+ *.f90 \
+ *.f95 \
+ *.f03 \
+ *.f08 \
+ *.f \
+ *.for \
+ *.tcl \
+ *.vhd \
+ *.vhdl \
+ *.ucf \
+ *.qsf
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE = build \
+ output
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+#
+#
+# where is the value of the INPUT_FILTER tag, and is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse-libclang=ON option for CMake.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLSALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLSALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = YES
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# If the EXT_LINKSWINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKSWINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use + S
+# (what the is depends on the OS and browser, but it is typically
+# , /, or both). Inside the search box use the to jump into the search results window, the results can be navigated
+# using the . Press to select an item or to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing +. Also here use the
+# to select a filter and or to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: YES.
+
+HAVE_DOT = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
+# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
+# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/components/connectivity/iot-hub-device-c-sdk/LICENSE b/components/connectivity/iot-hub-device-c-sdk/LICENSE
new file mode 100644
index 00000000..4ee2353e
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/LICENSE
@@ -0,0 +1,36 @@
+Tencent is pleased to support the open source community by making IoT Hub available.
+
+Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
+
+If you have downloaded a copy of the IoT Hub binary from Tencent, please note that the IoT Hub binary is licensed under the MIT License.
+
+If you have downloaded a copy of the IoT Hub source code from Tencent, please note that IoT Hub source code is licensed under the MIT License,
+
+except for the third-party components listed below which are subject to different license terms. Your integration of IoT Hub into your own projects may require compliance with the MIT License,
+
+as well as the other licenses applicable to the third-party components included within IoT Hub.
+
+A copy of the MIT License is included in this file.
+
+--------------------------------------------------------------------
+
+Terms of the MIT License:
+--------------------------------------------------------------------
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/components/connectivity/iot-hub-device-c-sdk/README.md b/components/connectivity/iot-hub-device-c-sdk/README.md
new file mode 100644
index 00000000..fab8f993
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/README.md
@@ -0,0 +1,174 @@
+
+# 腾讯云物联网设备端 C-SDK
+
+腾讯云物联网设备端 C-SDK 依靠安全且性能强大的数据通道,为物联网领域开发人员提供设备端快速接入云端,并和云端进行双向通信的能力。
+
+本SDK仅适用于物联网通信平台IoT Hub,对于物联网开发平台,请访问 [IoT Explorer C-SDK](https://github.com/tencentyun/qcloud-iot-explorer-sdk-embedded-c)
+
+## 一、SDK 目录结构简介
+
+| 名称 | 说明 |
+| ------------------ | ------------------------------------------------------------ |
+| CMakeLists.txt | cmake编译描述文件 |
+| cmake_build.sh | Linux下使用cmake的编译脚本 |
+| docs | 文档目录 |
+| 3rd | 第三方软件包组件,如mbedtls |
+| config | SDK功能配置以及编译工具链配置 |
+| include | 提供给用户使用的外部头文件 |
+| platform | 平台相关的源码文件,目前提供了针对不同OS(Linux/Windows/FreeRTOS/nonOS),TLS(mbedtls)以及AT模组下的实现 |
+| common | SDK通用组件库 |
+| services | SDK提供的服务源码,以模块独立存在 |
+
+- `config/settings`可选择相应的配置脚本,以根据需要裁剪SDK
+- `config/toolchains`可配置相应的编译工具链,以及选择平台
+
+## 二、SDK 各模块介绍
+
+| 模块名 | 所在目录 | 功能 |
+| ------------ | ------ | ------ |
+| mqtt_packet | common/mqtt_packet | 通用组件,实现MQTT控制包的序列化和反序列化 |
+| utils | common/utils | 通用组件,实现列表、日志等通用操作 |
+| cryptology | common/cryptology | 通用组件,实现SDK所需密码学算法 |
+| mqtt_client | common/mqtt_packet | 服务组件,实现MQTT客户端 |
+
+## 三、快速体验
+
+### 1. 安装环境
+
+以下以ubuntu为例子,安装`cmake`和`gcc`。
+
+```bash
+apt-get install cmake gcc g++
+```
+
+### 2. 创建设备
+
+参考[设备接入准备](https://cloud.tencent.com/document/product/634/14442#null)创建**密钥认证**设备,获取设备信息用作连接平台。
+
+- 产品ID
+
+
+
+- 设备名和设备密钥
+
+
+
+### 3. 修改设备信息
+
+编辑`platform/os/Linux/HAL_Device_linux.c`,修改设备信息:
+
+```c
+static char sg_product_id[MAX_SIZE_OF_PRODUCT_ID + 1] = "YOUR_PRODUCT_ID";
+static char sg_device_name[MAX_SIZE_OF_DEVICE_NAME + 1] = "YOUR_DEVICE_NAME";
+static char sg_device_secret[MAX_SIZE_OF_DEVICE_SECRET + 1] = "YOUR_IOT_PSK";
+```
+
+### 4. 编译运行
+
+运行根目录下`./cmake_build.sh`进行编译,然后运行`./output/bin/mqtt_sample`即可快速体验。
+
+## 四、代码贡献
+
+请仔细阅读[SDK代码规范说明](./docs/SDK代码规范说明.md)和[SDK代码格式说明](./docs/SDK代码格式说明)。
+
+`vscode`编辑器请安装以下插件,并配置:
+
+- `C/C++`:支持C语言语法和`clang-format`
+- `Code Spell Checker`:检查单词拼写,以及单词纠正
+- `Doxygen Documentation Generator`:在`settings.json`中添加以下内容,以支持`doxygen`
+
+```json
+ "doxdocgen.generic.authorEmail": "fancyxu@tencent.com",
+ "doxdocgen.generic.authorName": "fancyxu",
+ "doxdocgen.generic.authorTag": "@author {author} ({email})",
+ "doxdocgen.file.versionTag": "@version 1.0",
+ "doxdocgen.file.copyrightTag": [
+ "@copyright",
+ "",
+ "Tencent is pleased to support the open source community by making IoT Hub available.",
+ "Copyright(C) 2018 - {year} THL A29 Limited, a Tencent company.All rights reserved.",
+ "",
+ "Licensed under the MIT License(the \"License\"); you may not use this file except in",
+ "compliance with the License. You may obtain a copy of the License at",
+ "http://opensource.org/licenses/MIT",
+ "",
+ "Unless required by applicable law or agreed to in writing, software distributed under the License is",
+ "distributed on an \"AS IS\" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,",
+ "either express or implied. See the License for the specific language governing permissions and",
+ "limitations under the License.",
+ ],
+ "doxdocgen.file.customTag": [
+ "@par Change Log:",
+ "",
+ "Date Version Author Description",
+ " {date} 1.0 {author} first commit",
+ "
",
+ ],
+ "doxdocgen.file.fileOrder": [
+ "copyright",
+ "empty",
+ "file",
+ "brief",
+ "author",
+ "version",
+ "date",
+ "empty",
+ "custom"
+ ]
+```
+
+## 五、自动化文档生成
+
+### 1. 安装 doxygen
+
+```bash
+apt-get install doxygen graphviz
+```
+
+### 2. 生成 doxygen
+
+```bash
+doxygen Doxyfile
+```
+
+### 3. 浏览
+
+打开`output/html/index.html`,即可浏览项目中的接口和变量信息。
+
+## 六、单元测试
+
+### 1. 安装 google test
+
+```bash
+apt-get install googletest gcovr
+cd /usr/src/googletest
+mkdir -p build
+cd build
+cmake .. && make && make install
+```
+
+### 2. 安装并运行 mosquitto
+
+```bash
+apt-get install mosquitto
+mosquitto -c ./config/mosquitto/mosquitto.conf
+```
+
+### 3. 打开测试选项
+
+```cmake
+set(CONFIG_IOT_TEST ON)
+```
+
+### 4. 运行单元测试
+
+```bash
+./output/bin/iot_sdk_test
+```
+
+## 学习参考
+
+1. [腾讯云物联网设备端学习系列](https://cloud.tencent.com/developer/article/1789776)
+2. [googletest](https://github.com/google/googletest)
+3. [git 基本技能学习](https://github.com/xyfancy/GitBasicSkills)
+4. [mbedtls](https://github.com/ARMmbed/mbedtls)
diff --git a/components/connectivity/iot-hub-device-c-sdk/app/data_template/CMakeLists.txt b/components/connectivity/iot-hub-device-c-sdk/app/data_template/CMakeLists.txt
new file mode 100644
index 00000000..31d7a755
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/app/data_template/CMakeLists.txt
@@ -0,0 +1,3 @@
+file(GLOB src_app ${CMAKE_CURRENT_SOURCE_DIR}/*.c)
+add_executable(app_data_template ${src_app})
+target_link_libraries(app_data_template ${libsdk})
diff --git a/components/connectivity/iot-hub-device-c-sdk/app/data_template/data_template_app.c b/components/connectivity/iot-hub-device-c-sdk/app/data_template/data_template_app.c
new file mode 100644
index 00000000..742a463e
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/app/data_template/data_template_app.c
@@ -0,0 +1,352 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file data_template_sample.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-09-27
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-09-27 1.0 fancyxu first commit
+ *
+ */
+
+#include
+#include
+#include
+#include
+
+#include "qcloud_iot_common.h"
+#include "qcloud_iot_explorer.h"
+#include "data_template_config.h"
+
+#include "utils_log.h"
+
+/**
+ * @brief MQTT event callback, @see MQTTEventHandleFun
+ *
+ * @param[in] client pointer to mqtt client
+ * @param[in] handle_context context
+ * @param[in] msg msg
+ */
+static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
+{
+ MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
+ uintptr_t packet_id = (uintptr_t)msg->msg;
+
+ switch (msg->event_type) {
+ case MQTT_EVENT_UNDEF:
+ Log_i("undefined event occur.");
+ break;
+
+ case MQTT_EVENT_DISCONNECT:
+ Log_i("MQTT disconnect.");
+ break;
+
+ case MQTT_EVENT_RECONNECT:
+ Log_i("MQTT reconnect.");
+ break;
+
+ case MQTT_EVENT_PUBLISH_RECEIVED:
+ Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
+ mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
+ mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
+ break;
+
+ case MQTT_EVENT_SUBSCRIBE_SUCCESS:
+ Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
+ Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_SUBSCRIBE_NACK:
+ Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
+ Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
+ Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_UNSUBSCRIBE_NACK:
+ Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_PUBLISH_SUCCESS:
+ Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_PUBLISH_TIMEOUT:
+ Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_PUBLISH_NACK:
+ Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ default:
+ Log_i("Should NOT arrive here.");
+ break;
+ }
+}
+
+/**
+ * @brief Setup MQTT construct parameters.
+ *
+ * @param[in,out] initParams @see MQTTInitParams
+ * @param[in] device_info @see DeviceInfo
+ */
+static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
+{
+ init_params->device_info = device_info;
+ init_params->event_handle.h_fp = _mqtt_event_handler;
+}
+
+// ----------------------------------------------------------------------------
+// Data template callback
+// ----------------------------------------------------------------------------
+static void _handle_property_callback(void *client, int is_get_status)
+{
+ for (UsrPropertyIndex i = USR_PROPERTY_INDEX_POWER_SWITCH; i <= USR_PROPERTY_INDEX_POWER; i++) {
+ if (usr_data_template_property_status_get(i)) {
+ DataTemplatePropertyValue value;
+ switch (i) {
+ case USR_PROPERTY_INDEX_POWER_SWITCH:
+ case USR_PROPERTY_INDEX_COLOR:
+ case USR_PROPERTY_INDEX_BRIGHTNESS: // read only, just for example
+ case USR_PROPERTY_INDEX_POWER: // read only, just for example
+ value = usr_data_template_property_value_get(i);
+ Log_d("recv %s:%d", usr_data_template_property_key_get(i), value.value_int);
+ break;
+ case USR_PROPERTY_INDEX_NAME: // read only, just for example
+ value = usr_data_template_property_value_get(i);
+ Log_d("recv %s:%s", usr_data_template_property_key_get(i), value.value_string);
+ break;
+ case USR_PROPERTY_INDEX_POSITION: // read only, just for example
+ for (UsrPropertyPositionIndex j = USR_PROPERTY_POSITION_INDEX_LONGITUDE;
+ j <= USR_PROPERTY_POSITION_INDEX_LATITUDE; j++) {
+ value = usr_data_template_property_struct_value_get(i, j);
+ Log_d("recv %s:%d", usr_data_template_property_struct_key_get(i, j), value.value_int);
+ }
+ break;
+ }
+ usr_data_template_property_status_reset(i);
+ }
+ }
+}
+
+static void _method_control_callback(UtilsJsonValue client_token, UtilsJsonValue params, void *usr_data)
+{
+ char buf[256];
+ Log_i("recv msg[%.*s]: params=%.*s", client_token.value_len, client_token.value, params.value_len, params.value);
+ IOT_DataTemplate_PropertyControlReply(usr_data, buf, sizeof(buf), 0, client_token);
+ usr_data_template_property_parse(params);
+ _handle_property_callback(usr_data, 0);
+}
+
+static void _method_get_status_reply_callback(UtilsJsonValue client_token, int code, UtilsJsonValue reported,
+ UtilsJsonValue control, void *usr_data)
+{
+ char buf[256];
+ Log_i("recv msg[%.*s]: code=%d|reported=%.*s|control=%.*s", client_token.value_len, client_token.value, code,
+ reported.value_len, STRING_PTR_PRINT_SANITY_CHECK(reported.value), control.value_len,
+ STRING_PTR_PRINT_SANITY_CHECK(control.value));
+ usr_data_template_property_parse(control);
+ IOT_DataTemplate_PropertyClearControl(usr_data, buf, sizeof(buf));
+ _handle_property_callback(usr_data, 1);
+}
+
+static void _method_action_callback(UtilsJsonValue client_token, UtilsJsonValue action_id, UtilsJsonValue params,
+ void *usr_data)
+{
+ UsrActionIndex index;
+ int rc;
+ DataTemplatePropertyValue value_time, value_color, value_total_time;
+
+ char buf[256];
+
+ Log_i("recv msg[%.*s]: action_id=%.*s|params=%.*s", client_token.value_len, client_token.value, action_id.value_len,
+ action_id.value, params.value_len, params.value);
+
+ rc = usr_data_template_action_parse(action_id, params, &index);
+ if (rc) {
+ return;
+ }
+
+ switch (index) {
+ case USR_ACTION_INDEX_LIGHT_BLINK:
+ value_time = usr_data_template_action_input_value_get(USR_ACTION_INDEX_LIGHT_BLINK,
+ USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME);
+ value_color = usr_data_template_action_input_value_get(USR_ACTION_INDEX_LIGHT_BLINK,
+ USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR);
+ value_total_time = usr_data_template_action_input_value_get(USR_ACTION_INDEX_LIGHT_BLINK,
+ USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME);
+ Log_i("light[%d] blink %d every %d s ", value_color.value_enum, value_time.value_int,
+ value_total_time.value_int);
+ usr_data_template_action_reply(usr_data, buf, sizeof(buf), index, client_token, 0, "{\"err_code\":0}");
+ break;
+ default:
+ break;
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Data template upstream
+// ----------------------------------------------------------------------------
+
+/**
+ * @brief Report status.
+ *
+ * @param[in,out] client pointer to mqtt client
+ */
+static void _cycle_report(void *client)
+{
+ char buf[256];
+ static QcloudIotTimer sg_cycle_report_timer;
+ if (IOT_Timer_Expired(&sg_cycle_report_timer)) {
+ usr_data_template_event_post(client, buf, sizeof(buf), USR_EVENT_INDEX_STATUS_REPORT,
+ "{\"status\":0,\"message\":\"ok\"}");
+ IOT_Timer_Countdown(&sg_cycle_report_timer, 60);
+ }
+}
+
+/**
+ * @brief Init usr data template and data.
+ *
+ */
+static void _usr_init(void)
+{
+ usr_data_template_init();
+
+ DataTemplatePropertyValue value;
+ value.value_int = 0;
+ usr_data_template_property_value_set(USR_PROPERTY_INDEX_POWER_SWITCH, value);
+ value.value_enum = 0;
+ usr_data_template_property_value_set(USR_PROPERTY_INDEX_COLOR, value);
+ value.value_int = 10;
+ usr_data_template_property_value_set(USR_PROPERTY_INDEX_BRIGHTNESS, value);
+ value.value_string = "light";
+ usr_data_template_property_value_set(USR_PROPERTY_INDEX_NAME, value);
+ value.value_int = 30;
+ usr_data_template_property_struct_value_set(USR_PROPERTY_INDEX_POSITION, USR_PROPERTY_POSITION_INDEX_LONGITUDE,
+ value);
+ value.value_int = 30;
+ usr_data_template_property_struct_value_set(USR_PROPERTY_INDEX_POSITION, USR_PROPERTY_POSITION_INDEX_LATITUDE,
+ value);
+ value.value_string = "high";
+ usr_data_template_property_value_set(USR_PROPERTY_INDEX_POWER, value);
+}
+
+// ----------------------------------------------------------------------------
+// Main
+// ----------------------------------------------------------------------------
+static int sg_main_exit = 0;
+
+#ifdef __linux__
+
+#include
+#include
+#include
+
+static void _main_exit(int sig)
+{
+ Log_e("demo exit by signal:%d", sig);
+ sg_main_exit = 1;
+}
+#endif
+
+int main(int argc, char **argv)
+{
+#ifdef __linux__
+ signal(SIGINT, _main_exit);
+#endif
+
+ int rc;
+
+ char buf[1024];
+
+ // init log level
+ LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
+ utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
+
+ DeviceInfo device_info;
+
+ rc = HAL_GetDevInfo((void *)&device_info);
+ if (rc) {
+ Log_e("get device info failed: %d", rc);
+ return rc;
+ }
+
+ _usr_init();
+
+ // init connection
+ MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
+ _setup_connect_init_params(&init_params, &device_info);
+
+ // create MQTT client and connect with server
+ void *client = IOT_MQTT_Construct(&init_params);
+ if (client) {
+ Log_i("Cloud Device Construct Success");
+ } else {
+ Log_e("MQTT Construct failed!");
+ return QCLOUD_ERR_FAILURE;
+ }
+
+ // subscribe normal topics and wait result
+ IotDataTemplateCallback callback = DEFAULT_DATA_TEMPLATE_CALLBACK;
+ callback.property_callback.method_control_callback = _method_control_callback;
+ callback.property_callback.method_get_status_reply_callback = _method_get_status_reply_callback;
+ callback.action_callback.method_action_callback = _method_action_callback;
+
+ rc = IOT_DataTemplate_Init(client, callback, client);
+ if (rc) {
+ Log_e("Client Subscribe Topic Failed: %d", rc);
+ return rc;
+ }
+
+ IOT_DataTemplate_PropertyGetStatus(client, buf, sizeof(buf));
+
+ do {
+ rc = IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT);
+ switch (rc) {
+ case QCLOUD_RET_SUCCESS:
+ break;
+ case QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT:
+ continue;
+ case QCLOUD_RET_MQTT_RECONNECTED:
+ IOT_DataTemplate_PropertyGetStatus(client, buf, sizeof(buf));
+ break;
+ default:
+ Log_e("Exit loop caused of errCode:%d", rc);
+ goto exit;
+ }
+ _cycle_report(client);
+ usr_data_template_property_report(client, buf, sizeof(buf));
+ } while (!sg_main_exit);
+
+exit:
+ IOT_DataTemplate_Deinit(client);
+ rc |= IOT_MQTT_Destroy(&client);
+ utils_log_deinit();
+ return rc;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/app/data_template/data_template_config.c b/components/connectivity/iot-hub-device-c-sdk/app/data_template/data_template_config.c
new file mode 100644
index 00000000..d08e3279
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/app/data_template/data_template_config.c
@@ -0,0 +1,410 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file data_template_config.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-10-09
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-10-09 1.0 fancyxu first commit
+ *
+ */
+
+#include "data_template_config.h"
+
+/**
+ * @brief Set property value.
+ *
+ * @param[in,out] property pointer to property
+ * @param[in] value value to set, should match with property type
+ * @return 0 for success.
+ */
+static int _set_property_value(DataTemplateProperty* property, UtilsJsonValue value)
+{
+ int i, rc = 0;
+
+ switch (property->type) {
+ case DATA_TEMPLATE_TYPE_INT:
+ case DATA_TEMPLATE_TYPE_ENUM:
+ case DATA_TEMPLATE_TYPE_BOOL:
+ return utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_INT32, &property->value.value_int);
+ case DATA_TEMPLATE_TYPE_TIME:
+ return utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_UINT32, &property->value.value_time);
+ case DATA_TEMPLATE_TYPE_STRING:
+ case DATA_TEMPLATE_TYPE_STRING_ENUM:
+ if (!property->value.value_string) { // no need copy
+ return 0;
+ }
+ strncpy(property->value.value_string, value.value, value.value_len);
+ property->value.value_string[value.value_len] = '\0';
+ return 0;
+ case DATA_TEMPLATE_TYPE_FLOAT:
+ return utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_FLOAT, &property->value.value_float);
+ case DATA_TEMPLATE_TYPE_STRUCT:
+ for (i = 0; i < property->value.value_struct.count; i++) {
+ rc |= _set_property_value(property->value.value_struct.property + i, value);
+ }
+ return rc;
+ case DATA_TEMPLATE_TYPE_ARRAY:
+ Log_e("array type is not supportted yet!");
+ return -1;
+ default:
+ Log_e("unkown type!");
+ return -1;
+ }
+}
+
+/**
+ * @brief Get property node in json.
+ *
+ * @param[out] json_buf buffer to put node
+ * @param[in] buf_len buffer length
+ * @param[in] property property to get node
+ * @return 0 for success.
+ */
+static int _get_property_node(char* json_buf, int buf_len, const DataTemplateProperty* property)
+{
+ int len, i;
+ switch (property->type) {
+ case DATA_TEMPLATE_TYPE_INT:
+ case DATA_TEMPLATE_TYPE_ENUM:
+ case DATA_TEMPLATE_TYPE_BOOL:
+ return HAL_Snprintf(json_buf, buf_len, "\"%s\":%d", property->key, property->value.value_int);
+ case DATA_TEMPLATE_TYPE_TIME:
+ return HAL_Snprintf(json_buf, buf_len, "\"%s\":%u", property->key, property->value.value_time);
+ case DATA_TEMPLATE_TYPE_STRING:
+ case DATA_TEMPLATE_TYPE_STRING_ENUM:
+ if (!property->value.value_string) {
+ return 0;
+ }
+ return HAL_Snprintf(json_buf, buf_len, "\"%s\":\"%s\"", property->key, property->value.value_string);
+ case DATA_TEMPLATE_TYPE_FLOAT:
+ return HAL_Snprintf(json_buf, buf_len, "\"%s\":%f", property->key, property->value.value_float);
+ case DATA_TEMPLATE_TYPE_STRUCT:
+ len = HAL_Snprintf(json_buf, buf_len, "\"%s\":{", property->key);
+ for (i = 0; i < property->value.value_struct.count; i++) {
+ len += _get_property_node(json_buf + len, buf_len - len, property->value.value_struct.property + i);
+ json_buf[len++] = ',';
+ }
+ json_buf[--len] = '}';
+ return len + 1;
+ case DATA_TEMPLATE_TYPE_ARRAY:
+ Log_e("array type is not supportted yet!");
+ return 0;
+ default:
+ Log_e("unkown type!");
+ return 0;
+ }
+}
+
+/**
+ * @brief Parse property array.
+ *
+ * @param[in] json_buf json string to parse
+ * @param[in] buf_len json len
+ * @param[in,out] properties pointer to property array
+ * @param[in] property_count count of property
+ */
+static void _parse_property_array(const char* json_buf, int buf_len, DataTemplateProperty* properties,
+ int property_count)
+{
+ DataTemplateProperty* property;
+ UtilsJsonValue value;
+ for (int i = 0; i < property_count; i++) {
+ property = &properties[i];
+ if (!property->is_rw) { // read only property should not be processed
+ continue;
+ }
+ if (!utils_json_value_get(property->key, strlen(property->key), json_buf, buf_len, &value)) {
+ property->is_change = property->need_report = !_set_property_value(property, value);
+ }
+ }
+}
+
+#include "data_template_config_src_c.include"
+
+/**************************************************************************************
+ * API
+ **************************************************************************************/
+
+/**
+ * @brief Init user data template(property/event/action).
+ *
+ */
+void usr_data_template_init(void)
+{
+ _init_data_template_property();
+ _init_data_template_event();
+ _init_data_template_action();
+}
+
+/**
+ * @brief Get property value.
+ *
+ * @param[in] index @see UsrPropertyIndex
+ * @return @see DataTemplatePropertyValue
+ */
+DataTemplatePropertyValue usr_data_template_property_value_get(UsrPropertyIndex index)
+{
+ return sg_usr_data_template_property[index].value;
+}
+
+/**
+ * @brief Set property value.
+ *
+ * @param[in] index @see UsrPropertyIndex
+ * @param[in] value @see DataTemplatePropertyValue, @note value should match property type.
+ */
+void usr_data_template_property_value_set(UsrPropertyIndex index, DataTemplatePropertyValue value)
+{
+ if (sg_usr_data_template_property[index].type == DATA_TEMPLATE_TYPE_STRING ||
+ sg_usr_data_template_property[index].type == DATA_TEMPLATE_TYPE_STRING_ENUM) {
+ size_t value_len = strlen(value.value_string);
+ strncpy(sg_usr_data_template_property[index].value.value_string, value.value_string, value_len);
+ sg_usr_data_template_property[index].value.value_string[value_len] = '\0';
+ } else {
+ sg_usr_data_template_property[index].value = value;
+ }
+ sg_usr_data_template_property[index].need_report = 1;
+}
+
+/**
+ * @brief Get property(struct) value.
+ *
+ * @param[in] struct_index @see UsrPropertyIndex, @note DATA_TEMPLATE_TYPE_STRUCT is required here.
+ * @param[in] property_index depends on which struct
+ * @return @see DataTemplatePropertyValue
+ */
+DataTemplatePropertyValue usr_data_template_property_struct_value_get(UsrPropertyIndex struct_index, int property_index)
+{
+ return sg_usr_data_template_property[struct_index].value.value_struct.property[property_index].value;
+}
+
+/**
+ * @brief Set property(struct) value.
+ *
+ * @param[in] struct_index @see UsrPropertyIndex, @note DATA_TEMPLATE_TYPE_STRUCT is required here.
+ * @param[in] property_index depends on which struct
+ * @param[in] value @see DataTemplatePropertyValue, @note value should match property type.
+ */
+void usr_data_template_property_struct_value_set(UsrPropertyIndex struct_index, int property_index,
+ DataTemplatePropertyValue value)
+{
+ if (sg_usr_data_template_property[struct_index].value.value_struct.property[property_index].type ==
+ DATA_TEMPLATE_TYPE_STRING ||
+ sg_usr_data_template_property[struct_index].value.value_struct.property[property_index].type ==
+ DATA_TEMPLATE_TYPE_STRING_ENUM) {
+ size_t value_len = strlen(value.value_string);
+ char* dst_str =
+ sg_usr_data_template_property[struct_index].value.value_struct.property[property_index].value.value_string;
+ strncpy(dst_str, value.value_string, value_len);
+ dst_str[value_len] = '\0';
+ } else {
+ sg_usr_data_template_property[struct_index].value.value_struct.property[property_index].value = value;
+ }
+ sg_usr_data_template_property[struct_index].need_report = 1;
+}
+
+/**
+ * @brief Parse control message and set property value.
+ *
+ * @param[in] params params filed of control message
+ */
+void usr_data_template_property_parse(UtilsJsonValue params)
+{
+ _parse_property_array(params.value, params.value_len, sg_usr_data_template_property, TOTAL_USR_PROPERTY_COUNT);
+}
+
+/**
+ * @brief Get property status.
+ *
+ * @param[in] index @see UsrPropertyIndex
+ * @return if property is changed
+ */
+int usr_data_template_property_status_get(UsrPropertyIndex index)
+{
+ return sg_usr_data_template_property[index].is_change;
+}
+
+/**
+ * @brief Reset property status.
+ *
+ * @param[in] index @see UsrPropertyIndex
+ */
+void usr_data_template_property_status_reset(UsrPropertyIndex index)
+{
+ sg_usr_data_template_property[index].is_change = 0;
+}
+
+/**
+ * @brief Get property type.
+ *
+ * @param[in] index @see UsrPropertyIndex
+ * @return @see DataTemplatePropertyType
+ */
+DataTemplatePropertyType usr_data_template_property_type_get(UsrPropertyIndex index)
+{
+ return sg_usr_data_template_property[index].type;
+}
+
+/**
+ * @brief Get property key.
+ *
+ * @param[in] index @see UsrPropertyIndex
+ * @return key string
+ */
+const char* usr_data_template_property_key_get(UsrPropertyIndex index)
+{
+ return sg_usr_data_template_property[index].key;
+}
+
+/**
+ * @brief Get property(struct) key.
+ *
+ * @param[in] struct_index @see UsrPropertyIndex, @note DATA_TEMPLATE_TYPE_STRUCT is required here.
+ * @param[in] property_index depends on which struct
+ * @return key string
+ */
+const char* usr_data_template_property_struct_key_get(UsrPropertyIndex struct_index, int property_index)
+{
+ return sg_usr_data_template_property[struct_index].value.value_struct.property[property_index].key;
+}
+
+/**
+ * @brief Report all the properties needed report.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] buf buffer to report
+ * @param[in] buf_len buffer length
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int usr_data_template_property_report(void* client, char* buf, int buf_len)
+{
+ char params[1024];
+ memset(params, 0, sizeof(params));
+ params[0] = '{';
+ int offset, param_offset = 1;
+ for (int i = 0; i < TOTAL_USR_PROPERTY_COUNT; i++) {
+ DataTemplateProperty* property = &sg_usr_data_template_property[i];
+ if (property->need_report) {
+ offset = _get_property_node(params + param_offset, sizeof(params) - param_offset, property);
+ if (offset) {
+ param_offset += offset;
+ params[param_offset++] = ',';
+ }
+ property->need_report = 0;
+ }
+ }
+ params[--param_offset] = '}';
+
+ if (param_offset) {
+ return IOT_DataTemplate_PropertyReport(client, buf, buf_len, params);
+ }
+ return QCLOUD_RET_SUCCESS;
+}
+
+/**
+ * @brief Post event.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] buf buffer to report
+ * @param[in] buf_len buffer length
+ * @param[in] id @see UsrEventIndex
+ * @param[in] params user should construct params according to event.
+ */
+void usr_data_template_event_post(void* client, char* buf, int buf_len, UsrEventIndex id, const char* params)
+{
+ if (params) {
+ sg_usr_data_template_event[id].params = params;
+ }
+ IOT_DataTemplate_EventPost(client, buf, buf_len, sg_usr_data_template_event[id]);
+}
+
+/**
+ * @brief Parse action message and set action input params.
+ *
+ * @param[in] action_id action id
+ * @param[in] params params of action
+ * @param[out] index @see UsrActionIndex
+ * @return 0 for success, QCLOUD_ERR_JSON_PARSE for invalid json.
+ */
+int usr_data_template_action_parse(UtilsJsonValue action_id, UtilsJsonValue params, UsrActionIndex* index)
+{
+ DataTemplateProperty* property;
+
+ int input_property_count;
+
+ for (int i = 0; i < TOTAL_USR_ACTION_COUNT; i++) {
+ if (!strncmp(action_id.value, sg_usr_data_template_action[i].action_id, action_id.value_len)) {
+ property = sg_usr_data_template_action[i].input_struct.value_struct.property;
+ input_property_count = sg_usr_data_template_action[i].input_struct.value_struct.count;
+
+ // 1. reset need report
+ for (int j = 0; j < input_property_count; j++) {
+ property[j].need_report = 0;
+ }
+
+ // 2. parse
+ _parse_property_array(params.value, params.value_len, property, input_property_count);
+
+ // 3. check all the input params is set
+ for (int j = 0; j < input_property_count; j++) {
+ if (!property[j].need_report) {
+ return QCLOUD_ERR_JSON_PARSE;
+ }
+ }
+ *index = i;
+ return QCLOUD_RET_SUCCESS;
+ }
+ }
+ return QCLOUD_ERR_JSON_PARSE;
+}
+
+/**
+ * @brief Get input value, should call after usr_data_template_action_parse
+ *
+ * @param[in] index index get from usr_data_template_action_parse
+ * @param[in] property_index property index of action input params
+ * @return @see DataTemplatePropertyValue
+ */
+DataTemplatePropertyValue usr_data_template_action_input_value_get(UsrActionIndex index, int property_index)
+{
+ return sg_usr_data_template_action[index].input_struct.value_struct.property[property_index].value;
+}
+
+/**
+ * @brief Reply action, should call after parse action message.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] buf buffer to report
+ * @param[in] buf_len buffer length
+ * @param[in] index @see UsrActionIndex
+ * @param[in] client_token client token of action message
+ * @param[in] code result code for action, 0 for success
+ * @param[in] response action output params, user should construct params according to action
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int usr_data_template_action_reply(void* client, char* buf, int buf_len, UsrActionIndex index,
+ UtilsJsonValue client_token, int code, const char* response)
+{
+ sg_usr_data_template_action[index].reply.code = code;
+ sg_usr_data_template_action[index].reply.client_token = client_token;
+ sg_usr_data_template_action[index].reply.response = response;
+ return IOT_DataTemplate_ActionReply(client, buf, buf_len, sg_usr_data_template_action[index].reply);
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/app/data_template/data_template_config.h b/components/connectivity/iot-hub-device-c-sdk/app/data_template/data_template_config.h
new file mode 100644
index 00000000..1505c7a7
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/app/data_template/data_template_config.h
@@ -0,0 +1,266 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file data_template_config.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-10-09
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-10-09 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_APP_DATA_TEMPLATE_DATA_TEMPLATE_CONFIG_H_
+#define IOT_HUB_DEVICE_C_SDK_APP_DATA_TEMPLATE_DATA_TEMPLATE_CONFIG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "qcloud_iot_common.h"
+#include "qcloud_iot_data_template.h"
+
+/**
+ * @brief Type of property.
+ *
+ */
+typedef enum {
+ // basic type
+ DATA_TEMPLATE_TYPE_INT,
+ DATA_TEMPLATE_TYPE_ENUM,
+ DATA_TEMPLATE_TYPE_STRING_ENUM,
+ DATA_TEMPLATE_TYPE_FLOAT,
+ DATA_TEMPLATE_TYPE_BOOL,
+ DATA_TEMPLATE_TYPE_STRING,
+ DATA_TEMPLATE_TYPE_TIME,
+ // construct by basic type
+ DATA_TEMPLATE_TYPE_STRUCT,
+ DATA_TEMPLATE_TYPE_ARRAY,
+} DataTemplatePropertyType;
+
+/**
+ * @brief Declaration.
+ *
+ */
+typedef struct DataTemplateProperty DataTemplateProperty;
+typedef union DataTemplatePropertyValue DataTemplatePropertyValue;
+
+/**
+ * @brief Property value definition.
+ *
+ */
+union DataTemplatePropertyValue {
+ int value_int;
+ int value_enum;
+ char* value_string_enum;
+ float value_float;
+ int value_bool;
+ char* value_string;
+ uint32_t value_time;
+ struct {
+ DataTemplateProperty* property;
+ int count;
+ } value_struct;
+ DataTemplatePropertyValue* value_arrary; /**< not supportted yet */
+};
+
+/**
+ * @brief Property definition.
+ *
+ */
+struct DataTemplateProperty {
+ DataTemplatePropertyType type;
+ const char* key;
+ DataTemplatePropertyValue value;
+ int need_report;
+ int is_change;
+ int is_rw;
+};
+
+/**
+ * @brief Event definition.
+ *
+ */
+#define DataTemplateEvent IotDataTemplateEventData
+
+/**
+ * @brief Action definition.
+ *
+ */
+typedef struct {
+ const char* action_id;
+ DataTemplatePropertyValue input_struct;
+ IotDataTemplateActionReply reply;
+} DataTemplateAction;
+
+#include "data_template_config_header.include"
+
+/**************************************************************************************
+ * api for user data template
+ **************************************************************************************/
+
+/**
+ * @brief Init user data template(property/event/action).
+ *
+ */
+void usr_data_template_init(void);
+
+/**
+ * @brief Get property value.
+ *
+ * @param[in] index @see UsrPropertyIndex
+ * @return @see DataTemplatePropertyValue
+ */
+DataTemplatePropertyValue usr_data_template_property_value_get(UsrPropertyIndex index);
+
+/**
+ * @brief Set property value.
+ *
+ * @param[in] index @see UsrPropertyIndex
+ * @param[in] value @see DataTemplatePropertyValue, @note value should match property type.
+ */
+void usr_data_template_property_value_set(UsrPropertyIndex index, DataTemplatePropertyValue value);
+
+/**
+ * @brief Get property(struct) value.
+ *
+ * @param[in] struct_index @see UsrPropertyIndex, @note DATA_TEMPLATE_TYPE_STRUCT is required here.
+ * @param[in] property_index depends on which struct
+ * @return @see DataTemplatePropertyValue
+ */
+DataTemplatePropertyValue usr_data_template_property_struct_value_get(UsrPropertyIndex struct_index,
+ int property_index);
+
+/**
+ * @brief Set property(struct) value.
+ *
+ * @param[in] struct_index @see UsrPropertyIndex, @note DATA_TEMPLATE_TYPE_STRUCT is required here.
+ * @param[in] property_index depends on which struct
+ * @param[in] value @see DataTemplatePropertyValue, @note value should match property type.
+ */
+void usr_data_template_property_struct_value_set(UsrPropertyIndex struct_index, int property_index,
+ DataTemplatePropertyValue value);
+
+/**
+ * @brief Parse control message and set property value.
+ *
+ * @param[in] params params filed of control message
+ */
+void usr_data_template_property_parse(UtilsJsonValue params);
+
+/**
+ * @brief Get property status.
+ *
+ * @param[in] index @see UsrPropertyIndex
+ * @return need_report
+ */
+int usr_data_template_property_status_get(UsrPropertyIndex index);
+
+/**
+ * @brief Reset property status.
+ *
+ * @param[in] index @see UsrPropertyIndex
+ */
+void usr_data_template_property_status_reset(UsrPropertyIndex index);
+
+/**
+ * @brief Get property type.
+ *
+ * @param[in] index @see UsrPropertyIndex
+ * @return @see DataTemplatePropertyType
+ */
+DataTemplatePropertyType usr_data_template_property_type_get(UsrPropertyIndex index);
+
+/**
+ * @brief Get property key.
+ *
+ * @param[in] index @see UsrPropertyIndex
+ * @return key string
+ */
+const char* usr_data_template_property_key_get(UsrPropertyIndex index);
+
+/**
+ * @brief Get property(struct) key.
+ *
+ * @param[in] struct_index @see UsrPropertyIndex, @note DATA_TEMPLATE_TYPE_STRUCT is required here.
+ * @param[in] property_index depends on which struct
+ * @return key string
+ */
+const char* usr_data_template_property_struct_key_get(UsrPropertyIndex struct_index, int property_index);
+
+/**
+ * @brief Report all the properties needed report.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] buf buffer to report
+ * @param[in] buf_len buffer length
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int usr_data_template_property_report(void* client, char* buf, int buf_len);
+
+/**
+ * @brief Post event.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] buf buffer to report
+ * @param[in] buf_len buffer length
+ * @param[in] id @see UsrEventIndex
+ * @param[in] params user should construct params according to event.
+ */
+void usr_data_template_event_post(void* client, char* buf, int buf_len, UsrEventIndex id, const char* params);
+
+/**
+ * @brief Parse action message and set action input params.
+ *
+ * @param[in] action_id action id
+ * @param[in] params params of action
+ * @param[out] index @see UsrActionIndex
+ * @return 0 for success, QCLOUD_ERR_JSON_PARSE for invalid json.
+ */
+int usr_data_template_action_parse(UtilsJsonValue action_id, UtilsJsonValue params, UsrActionIndex* index);
+
+/**
+ * @brief Get input value, should call after usr_data_template_action_parse
+ *
+ * @param[in] index index get from usr_data_template_action_parse
+ * @param[in] property_index property index of action input params
+ * @return @see DataTemplatePropertyValue
+ */
+DataTemplatePropertyValue usr_data_template_action_input_value_get(UsrActionIndex index, int property_index);
+
+/**
+ * @brief Reply action, should call after parse action message.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] buf buffer to report
+ * @param[in] buf_len buffer length
+ * @param[in] index @see UsrActionIndex
+ * @param[in] client_token client token of action message
+ * @param[in] code result code for action, 0 for success
+ * @param[in] response action output params, user should construct params according to action
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int usr_data_template_action_reply(void* client, char* buf, int buf_len, UsrActionIndex index,
+ UtilsJsonValue client_token, int code, const char* response);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_APP_DATA_TEMPLATE_DATA_TEMPLATE_CONFIG_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/app/data_template/data_template_config_header.include b/components/connectivity/iot-hub-device-c-sdk/app/data_template/data_template_config_header.include
new file mode 100644
index 00000000..a7193454
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/app/data_template/data_template_config_header.include
@@ -0,0 +1,49 @@
+/*----------------- property index enum start -------------------*/
+
+typedef enum {
+ USR_PROPERTY_INDEX_POWER_SWITCH = 0,
+ USR_PROPERTY_INDEX_COLOR,
+ USR_PROPERTY_INDEX_BRIGHTNESS,
+ USR_PROPERTY_INDEX_NAME,
+ USR_PROPERTY_INDEX_POSITION,
+ USR_PROPERTY_INDEX_POWER,
+} UsrPropertyIndex;
+
+/*----------------- property index enum end -------------------*/
+
+/*----------------- position property index enum start -------------------*/
+
+typedef enum {
+ USR_PROPERTY_POSITION_INDEX_LONGITUDE = 0,
+ USR_PROPERTY_POSITION_INDEX_LATITUDE,
+} UsrPropertyPositionIndex;
+
+/*----------------- position property index enum end -------------------*/
+
+/*----------------- event index enum start -------------------*/
+
+typedef enum {
+ USR_EVENT_INDEX_STATUS_REPORT = 0,
+ USR_EVENT_INDEX_LOW_VOLTAGE,
+ USR_EVENT_INDEX_HARDWARE_FAULT,
+} UsrEventIndex;
+
+/*----------------- event index enum end -------------------*/
+
+/*----------------- action index enum start -------------------*/
+
+typedef enum {
+ USR_ACTION_INDEX_LIGHT_BLINK = 0,
+} UsrActionIndex;
+
+/*----------------- action index enum end -------------------*/
+
+/*----------------- action light_blink input index enum start -------------------*/
+
+typedef enum {
+ USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME = 0,
+ USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR,
+ USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME,
+} UsrActionLightblinkInputIndex;
+
+/*----------------- action light_blink input index enum end -------------------*/
diff --git a/components/connectivity/iot-hub-device-c-sdk/app/data_template/data_template_config_src_c.include b/components/connectivity/iot-hub-device-c-sdk/app/data_template/data_template_config_src_c.include
new file mode 100644
index 00000000..89364f0a
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/app/data_template/data_template_config_src_c.include
@@ -0,0 +1,160 @@
+// ----------------------------------------------------------------------------
+// user property
+// ----------------------------------------------------------------------------/
+
+#define TOTAL_USR_PROPERTY_STRUCT_POSITION_COUNT 2
+
+static DataTemplateProperty sg_usr_property_position[TOTAL_USR_PROPERTY_STRUCT_POSITION_COUNT];
+
+static void _init_data_template_property_position(void)
+{
+ sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LONGITUDE].value.value_int = 1;
+ sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LONGITUDE].key = "longitude";
+ sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LONGITUDE].type = DATA_TEMPLATE_TYPE_INT;
+ sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LONGITUDE].is_rw = 0;
+
+ sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LATITUDE].value.value_int = 1;
+ sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LATITUDE].key = "latitude";
+ sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LATITUDE].type = DATA_TEMPLATE_TYPE_INT;
+ sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LATITUDE].is_rw = 0;
+}
+
+#define TOTAL_USR_PROPERTY_COUNT 6
+
+static DataTemplateProperty sg_usr_data_template_property[TOTAL_USR_PROPERTY_COUNT];
+
+static void _init_data_template_property(void)
+{
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER_SWITCH].value.value_bool = 0;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER_SWITCH].key = "power_switch";
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER_SWITCH].type = DATA_TEMPLATE_TYPE_BOOL;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER_SWITCH].need_report = 1;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER_SWITCH].is_rw = 1;
+
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_COLOR].value.value_enum = 0;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_COLOR].key = "color";
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_COLOR].type = DATA_TEMPLATE_TYPE_ENUM;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_COLOR].need_report = 1;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_COLOR].is_rw = 1;
+
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_BRIGHTNESS].value.value_int = 1;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_BRIGHTNESS].key = "brightness";
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_BRIGHTNESS].type = DATA_TEMPLATE_TYPE_INT;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_BRIGHTNESS].need_report = 1;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_BRIGHTNESS].is_rw = 1;
+
+ static char sg_usr_property_name[64 + 1] = " ";
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_NAME].value.value_string = sg_usr_property_name;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_NAME].key = "name";
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_NAME].type = DATA_TEMPLATE_TYPE_STRING;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_NAME].need_report = 1;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_NAME].is_rw = 1;
+
+ _init_data_template_property_position();
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_POSITION].value.value_struct.property = sg_usr_property_position;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_POSITION].value.value_struct.count =
+ TOTAL_USR_PROPERTY_STRUCT_POSITION_COUNT;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_POSITION].key = "position";
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_POSITION].type = DATA_TEMPLATE_TYPE_STRUCT;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_POSITION].need_report = 1;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_POSITION].is_rw = 0;
+
+ static char sg_usr_property_power[6 + 1] = "high";
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER].value.value_string_enum = sg_usr_property_power;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER].key = "power";
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER].type = DATA_TEMPLATE_TYPE_STRING_ENUM;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER].need_report = 1;
+ sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER].is_rw = 1;
+}
+
+// ----------------------------------------------------------------------------
+// user event
+// ----------------------------------------------------------------------------
+
+/**
+ * @brief Sample of event status_report post params.
+ *
+ */
+static const char* sg_usr_event_status_report_params = "{\"status\":0,\"message\":\" \"}";
+
+/**
+ * @brief Sample of event low_voltage post params.
+ *
+ */
+static const char* sg_usr_event_low_voltage_params = "{\"voltage\":1}";
+
+/**
+ * @brief Sample of event hardware_fault post params.
+ *
+ */
+static const char* sg_usr_event_hardware_fault_params = "{\"name\":\" \",\"error_code\":1}";
+
+#define TOTAL_USR_EVENT_COUNT 3
+
+static DataTemplateEvent sg_usr_data_template_event[TOTAL_USR_EVENT_COUNT];
+
+static void _init_data_template_event(void)
+{
+ sg_usr_data_template_event[USR_EVENT_INDEX_STATUS_REPORT].event_id = "status_report";
+ sg_usr_data_template_event[USR_EVENT_INDEX_STATUS_REPORT].type = IOT_DATA_TEMPLATE_EVENT_TYPE_INFO;
+ sg_usr_data_template_event[USR_EVENT_INDEX_STATUS_REPORT].params = sg_usr_event_status_report_params;
+
+ sg_usr_data_template_event[USR_EVENT_INDEX_LOW_VOLTAGE].event_id = "low_voltage";
+ sg_usr_data_template_event[USR_EVENT_INDEX_LOW_VOLTAGE].type = IOT_DATA_TEMPLATE_EVENT_TYPE_ALERT;
+ sg_usr_data_template_event[USR_EVENT_INDEX_LOW_VOLTAGE].params = sg_usr_event_low_voltage_params;
+
+ sg_usr_data_template_event[USR_EVENT_INDEX_HARDWARE_FAULT].event_id = "hardware_fault";
+ sg_usr_data_template_event[USR_EVENT_INDEX_HARDWARE_FAULT].type = IOT_DATA_TEMPLATE_EVENT_TYPE_FAULT;
+ sg_usr_data_template_event[USR_EVENT_INDEX_HARDWARE_FAULT].params = sg_usr_event_hardware_fault_params;
+}
+
+// ----------------------------------------------------------------------------
+// user action
+// ----------------------------------------------------------------------------
+
+#define TOTAL_USR_ACTION_LIGHT_BLINK_INPUT_PARAMS_COUNT 3
+
+static DataTemplateProperty sg_usr_action_light_blink_input[TOTAL_USR_ACTION_LIGHT_BLINK_INPUT_PARAMS_COUNT];
+
+static void _init_data_template_action_light_blink_input(void)
+{
+ sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME].value.value_int = 0;
+ sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME].key = "time";
+ sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME].type = DATA_TEMPLATE_TYPE_INT;
+ sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME].is_rw = 1;
+
+ sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR].value.value_bool = 0;
+ sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR].key = "color";
+ sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR].type = DATA_TEMPLATE_TYPE_BOOL;
+ sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR].is_rw = 1;
+
+ sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME].value.value_int = 0;
+ sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME].key = "total_time";
+ sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME].type = DATA_TEMPLATE_TYPE_INT;
+ sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME].is_rw = 1;
+}
+
+/**
+ * @brief Sample of action light_blink reply.
+ *
+ */
+static IotDataTemplateActionReply sg_usr_action_light_blink_reply = {
+ .code = 0,
+ .client_token = {.value = "test_light_blink", .value_len = sizeof("test_light_blink")},
+ .response = "{\"err_code\":0}",
+};
+
+#define TOTAL_USR_ACTION_COUNT 1
+
+static DataTemplateAction sg_usr_data_template_action[TOTAL_USR_ACTION_COUNT];
+
+static void _init_data_template_action(void)
+{
+ _init_data_template_action_light_blink_input();
+ sg_usr_data_template_action[USR_ACTION_INDEX_LIGHT_BLINK].action_id = "light_blink";
+ sg_usr_data_template_action[USR_ACTION_INDEX_LIGHT_BLINK].input_struct.value_struct.property =
+ sg_usr_action_light_blink_input;
+ sg_usr_data_template_action[USR_ACTION_INDEX_LIGHT_BLINK].input_struct.value_struct.count =
+ TOTAL_USR_ACTION_LIGHT_BLINK_INPUT_PARAMS_COUNT;
+ sg_usr_data_template_action[USR_ACTION_INDEX_LIGHT_BLINK].reply = sg_usr_action_light_blink_reply;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/app/ota/CMakeLists.txt b/components/connectivity/iot-hub-device-c-sdk/app/ota/CMakeLists.txt
new file mode 100644
index 00000000..d3affdae
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/app/ota/CMakeLists.txt
@@ -0,0 +1,3 @@
+file(GLOB src_app ${CMAKE_CURRENT_SOURCE_DIR}/*.c)
+add_executable(app_ota ${src_app})
+target_link_libraries(app_ota ${libsdk})
diff --git a/components/connectivity/iot-hub-device-c-sdk/app/ota/ota_app.c b/components/connectivity/iot-hub-device-c-sdk/app/ota/ota_app.c
new file mode 100644
index 00000000..4f3e3473
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/app/ota/ota_app.c
@@ -0,0 +1,238 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file broadcast_sample.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-07-18
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-07-18 1.0 fancyxu first commit
+ *
+ */
+
+#include
+#include
+#include
+#include
+
+#include "qcloud_iot_common.h"
+#include "ota_downloader.h"
+
+#include "utils_log.h"
+
+/**
+ * @brief MQTT event callback, @see MQTTEventHandleFun
+ *
+ * @param[in] client pointer to mqtt client
+ * @param[in] handle_context context
+ * @param[in] msg msg
+ */
+static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
+{
+ MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
+ uintptr_t packet_id = (uintptr_t)msg->msg;
+
+ switch (msg->event_type) {
+ case MQTT_EVENT_UNDEF:
+ Log_i("undefined event occur.");
+ break;
+
+ case MQTT_EVENT_DISCONNECT:
+ Log_i("MQTT disconnect.");
+ break;
+
+ case MQTT_EVENT_RECONNECT:
+ Log_i("MQTT reconnect.");
+ break;
+
+ case MQTT_EVENT_PUBLISH_RECEIVED:
+ Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
+ mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
+ mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->payload_str));
+ break;
+ case MQTT_EVENT_SUBSCRIBE_SUCCESS:
+ Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
+ Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_SUBSCRIBE_NACK:
+ Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
+ Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
+ Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_UNSUBSCRIBE_NACK:
+ Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_PUBLISH_SUCCESS:
+ Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_PUBLISH_TIMEOUT:
+ Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_PUBLISH_NACK:
+ Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
+ break;
+ default:
+ Log_i("Should NOT arrive here.");
+ break;
+ }
+}
+
+/**
+ * @brief Setup MQTT construct parameters.
+ *
+ * @param[in,out] initParams @see MQTTInitParams
+ * @param[in] device_info @see DeviceInfo
+ */
+static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
+{
+ init_params->device_info = device_info;
+ init_params->event_handle.h_fp = _mqtt_event_handler;
+}
+
+// ----------------------------------------------------------------------------
+// OTA callback
+// ----------------------------------------------------------------------------
+
+void _update_firmware_callback(UtilsJsonValue version, UtilsJsonValue url, UtilsJsonValue md5sum, uint32_t file_size,
+ void *usr_data)
+{
+ Log_i("recv firmware: version=%.*s|url=%.*s|md5sum=%.*s|file_size=%u", version.value_len, version.value,
+ url.value_len, url.value, md5sum.value_len, md5sum.value, file_size);
+ // only one firmware one time is supportted now
+ OTAFirmwareInfo firmware_info;
+ memset(&firmware_info, 0, sizeof(OTAFirmwareInfo));
+ strncpy(firmware_info.version, version.value, version.value_len);
+ strncpy(firmware_info.md5sum, md5sum.value, md5sum.value_len);
+ firmware_info.file_size = file_size;
+ ota_downloader_info_set(&firmware_info, url.value, url.value_len);
+}
+
+// ----------------------------------------------------------------------------
+// Main
+// ----------------------------------------------------------------------------
+static int sg_main_exit = 0;
+
+#ifdef __linux__
+
+#include
+#include
+#include
+
+static void _main_exit(int sig)
+{
+ Log_e("demo exit by signal:%d\n", sig);
+ sg_main_exit = 1;
+}
+#endif
+
+int main(int argc, char **argv)
+{
+#ifdef __linux__
+ signal(SIGINT, _main_exit);
+#endif
+
+ int rc;
+
+ char buf[1024];
+
+ // init log level
+ LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
+ utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
+
+ DeviceInfo device_info;
+ rc = HAL_GetDevInfo((void *)&device_info);
+ if (rc) {
+ Log_e("get device info failed: %d", rc);
+ return rc;
+ }
+
+ // init connection
+ MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
+ _setup_connect_init_params(&init_params, &device_info);
+
+ // create MQTT client and connect with server
+ void *client = IOT_MQTT_Construct(&init_params);
+ if (client) {
+ Log_i("Cloud Device Construct Success");
+ } else {
+ Log_e("MQTT Construct failed!");
+ return QCLOUD_ERR_FAILURE;
+ }
+
+ // OTA init
+ IotOTAUpdateCallback ota_callback = {
+ .update_firmware_callback = _update_firmware_callback,
+ .report_version_reply_callback = NULL,
+ };
+
+ rc = IOT_OTA_Init(client, ota_callback, NULL);
+ if (rc) {
+ Log_e("OTA init failed!, rc=%d", rc);
+ return rc;
+ }
+
+ rc = IOT_OTA_ReportVersion(client, buf, sizeof(buf), QCLOUD_IOT_DEVICE_SDK_VERSION);
+ if (rc) {
+ Log_e("OTA report version failed!, rc=%d", rc);
+ return rc;
+ }
+
+ rc = ota_downloader_init(client);
+ if (rc) {
+ Log_e("OTA downloader init failed!, rc=%d", rc);
+ return rc;
+ }
+
+ do {
+ ota_downloader_process();
+
+ rc = IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT);
+ switch (rc) {
+ case QCLOUD_RET_SUCCESS:
+ break;
+ case QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT:
+ continue;
+ case QCLOUD_RET_MQTT_RECONNECTED:
+ break;
+ default:
+ Log_e("Exit loop caused of errCode:%d", rc);
+ goto exit;
+ }
+ } while (!sg_main_exit);
+exit:
+ ota_downloader_deinit();
+ IOT_OTA_Deinit(client);
+ rc = IOT_MQTT_Destroy(&client);
+ utils_log_deinit();
+ return rc;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/app/ota/ota_downloader.c b/components/connectivity/iot-hub-device-c-sdk/app/ota/ota_downloader.c
new file mode 100644
index 00000000..84280d80
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/app/ota/ota_downloader.c
@@ -0,0 +1,409 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file ota_downloader.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-10-20
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-10-20 1.0 fancyxu first commit
+ *
+ */
+
+#include "ota_downloader.h"
+#include "utils_log.h"
+#include "utils_md5.h"
+
+#define OTA_HTTP_TIMEOUT_MS 5000
+#define OTA_HTTP_BUF_SIZE 1024
+#define MAX_SIZE_OF_DOWNLOAD_URL 512
+
+/**
+ * @brief Break point info.
+ *
+ */
+typedef struct {
+ OTAFirmwareInfo file_id;
+ uint32_t downloaded_size;
+} OTADownloadInfo;
+
+/**
+ * @brief OTA downloader handle.
+ *
+ */
+typedef struct {
+ void* cos_download;
+ void* mqtt_client;
+ void* downloader;
+
+ OTAFirmwareInfo download_now;
+ OTADownloadInfo break_point;
+
+ char download_url[MAX_SIZE_OF_DOWNLOAD_URL];
+ uint8_t download_buff[OTA_HTTP_BUF_SIZE];
+ uint32_t download_size;
+ IotMd5Context download_md5_ctx;
+
+ OTADownloaderStatus status;
+} OTADownloaderHandle;
+
+/**
+ * @brief Handle.
+ *
+ */
+static OTADownloaderHandle sg_ota_downloader_handle = {0};
+
+// ----------------------------------------------------------------------------
+// Downloader function
+// ----------------------------------------------------------------------------
+
+// break point function
+
+/**
+ * @brief Read break point from file.
+ *
+ * @param[in,out] usr_data @see OTADownloaderHandle
+ * @return 0 for success
+ */
+static int _ota_break_point_init(void* usr_data)
+{
+ OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
+
+ sg_ota_downloader_handle.cos_download = NULL;
+ utils_md5_reset(&handle->download_md5_ctx);
+ memset(&handle->break_point, 0, sizeof(handle->break_point));
+
+ size_t len =
+ HAL_File_Read(OTA_BREAK_POINT_FILE_PATH, (uint8_t*)&handle->break_point, sizeof(handle->break_point), 0);
+ if (len != sizeof(handle->break_point)) {
+ Log_w("open break point file fail!");
+ }
+ return 0;
+}
+
+/**
+ * @brief Memset break point.
+ *
+ * @param[in,out] usr_data @see OTADownloaderHandle
+ */
+static void _ota_break_point_deinit(void* usr_data)
+{
+ OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
+ memset(&handle->break_point, 0, sizeof(handle->break_point));
+}
+
+/**
+ * @brief Set break point using download now info.
+ *
+ * @param[in,out] usr_data @see OTADownloaderHandle
+ * @return 0 for success
+ */
+static int _ota_break_point_set(void* usr_data)
+{
+ OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
+ // read break point info from flash
+ memset(&handle->break_point, 0, sizeof(handle->break_point));
+ memcpy(&handle->break_point.file_id, &handle->download_now, sizeof(handle->break_point.file_id));
+ return 0;
+}
+
+/**
+ * @brief Update break point and save.
+ *
+ * @param[in,out] usr_data @see OTADownloaderHandle
+ * @return 0 for success
+ */
+static int _ota_break_point_save(void* usr_data)
+{
+ OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
+
+ // update md5 sum & download_size
+ handle->break_point.downloaded_size += handle->download_size;
+ utils_md5_update(&handle->download_md5_ctx, handle->download_buff, handle->download_size);
+
+ // report progress
+ char buf[256];
+ int buf_len = sizeof(buf);
+ IOT_OTA_ReportProgress(handle->mqtt_client, buf, buf_len, IOT_OTA_REPORT_TYPE_DOWNLOADING,
+ handle->break_point.downloaded_size * 100 / handle->download_now.file_size,
+ handle->break_point.file_id.version);
+
+ // write to local, only write downloaded size is ok
+ size_t write_size =
+ HAL_File_Write(OTA_BREAK_POINT_FILE_PATH, (uint8_t*)&handle->break_point, sizeof(OTADownloadInfo), 0);
+ return write_size != sizeof(OTADownloadInfo);
+}
+
+/**
+ * @brief Check if break point matches download now info.
+ *
+ * @param[in,out] usr_data @see OTADownloaderHandle
+ * @return 0 for success
+ */
+static int _ota_break_point_check(void* usr_data)
+{
+ OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
+ // version should be the same, download size should not bigger than file size
+ Log_d("download now:%s,%d,%s", handle->download_now.version, handle->download_now.file_size,
+ handle->download_now.md5sum);
+ Log_d("break point:%s,%d,%d,%s", handle->break_point.file_id.version, handle->break_point.file_id.file_size,
+ handle->break_point.downloaded_size, handle->break_point.file_id.md5sum);
+ return strncmp(handle->break_point.file_id.version, handle->download_now.version, MAX_SIZE_OF_FW_VERSION) ||
+ handle->break_point.file_id.file_size != handle->download_now.file_size ||
+ handle->break_point.downloaded_size > handle->download_now.file_size ||
+ strncmp(handle->break_point.file_id.md5sum, handle->download_now.md5sum, 32);
+}
+
+/**
+ * @brief Calculate md5 sum according break point.
+ *
+ * @param[in,out] usr_data @see OTADownloaderHandle
+ * @return 0 for success
+ */
+static int _ota_break_point_restore(void* usr_data)
+{
+ OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
+
+ // update md5 according downloaded data
+ size_t rlen, total_read = 0, size = 0;
+
+ size = handle->break_point.downloaded_size;
+
+ while (size > 0) {
+ rlen = (size > OTA_HTTP_BUF_SIZE) ? OTA_HTTP_BUF_SIZE : size;
+ if (!HAL_File_Read(OTA_FILE_PATH, handle->download_buff, rlen, total_read)) {
+ Log_e("read data failed");
+ handle->break_point.downloaded_size = 0;
+ break;
+ }
+ utils_md5_update(&handle->download_md5_ctx, handle->download_buff, rlen);
+ size -= rlen;
+ total_read += rlen;
+ }
+ return 0;
+}
+
+// data download function
+
+/**
+ * @brief Init cos downloader.
+ *
+ * @param[in,out] usr_data @see OTADownloaderHandle
+ * @return 0 for success
+ */
+static int _ota_data_download_init(void* usr_data)
+{
+ OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
+ IotCosDownloadParams params = {
+ .url = handle->download_url,
+ .offset = handle->break_point.downloaded_size,
+ .file_size = handle->break_point.file_id.file_size,
+ .is_fragmentation = false,
+ .is_https_enabled = false,
+ };
+ handle->cos_download = IOT_COS_DownloadInit(¶ms);
+ return handle->cos_download ? 0 : -1;
+}
+
+/**
+ * @brief Deinit cos downloader.
+ *
+ * @param[in,out] usr_data @see OTADownloaderHandle
+ */
+static void _ota_data_download_deinit(void* usr_data)
+{
+ OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
+ IOT_COS_DownloadDeinit(handle->cos_download);
+}
+
+/**
+ * @brief Check if download finished.
+ *
+ * @param[in,out] usr_data @see OTADownloaderHandle
+ * @return 0 for success
+ */
+static int _ota_data_download_is_over(void* usr_data)
+{
+ // check download is over
+ OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
+ return handle->break_point.downloaded_size == handle->download_now.file_size;
+}
+
+/**
+ * @brief Download from cos server.
+ *
+ * @param[in,out] usr_data @see OTADownloaderHandle
+ * @return 0 for success
+ */
+static int _ota_data_download_recv(void* usr_data)
+{
+ // download data using http
+ OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
+ // TODO: https download
+ handle->download_size =
+ IOT_COS_DownloadFetch(handle->cos_download, handle->download_buff, OTA_HTTP_BUF_SIZE, OTA_HTTP_TIMEOUT_MS);
+ return handle->download_size;
+}
+
+/**
+ * @brief Sava firmware to file.
+ *
+ * @param[in,out] usr_data @see OTADownloaderHandle
+ * @return 0 for success
+ */
+static int _ota_data_download_save(void* usr_data)
+{
+ OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
+ return handle->download_size > 0 ? HAL_File_Write(OTA_FILE_PATH, handle->download_buff, handle->download_size,
+ handle->break_point.downloaded_size) != handle->download_size
+ : 0;
+}
+
+/**
+ * @brief Process download result.
+ *
+ * @param[in,out] usr_data @see OTADownloaderHandle
+ * @param[in] status status when finish download, @see UtilsDownloaderStatus
+ * @return 0 for success
+ */
+static int _ota_data_download_finish(void* usr_data, UtilsDownloaderStatus status)
+{
+ OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
+
+ int rc = 0;
+ char buf[256];
+ int buf_len = sizeof(buf);
+
+ switch (status) {
+ case UTILS_DOWNLOADER_STATUS_SUCCESS:
+ utils_md5_finish(&handle->download_md5_ctx);
+ // do something when ota finish;
+
+ int valid = !utils_md5_compare(&handle->download_md5_ctx, handle->download_now.md5sum);
+
+ if (!valid) {
+ memset(&handle->break_point, 0, sizeof(handle->break_point));
+ HAL_File_Write(OTA_BREAK_POINT_FILE_PATH, (uint8_t*)&handle->break_point, sizeof(handle->break_point),
+ 0);
+ }
+
+ rc = valid ? IOT_OTA_ReportProgress(handle->mqtt_client, buf, buf_len, IOT_OTA_REPORT_TYPE_UPGRADE_SUCCESS,
+ 0, handle->download_now.version)
+ : IOT_OTA_ReportProgress(handle->mqtt_client, buf, buf_len, IOT_OTA_REPORT_TYPE_MD5_NOT_MATCH, 0,
+ handle->download_now.version);
+ break;
+ case UTILS_DOWNLOADER_STATUS_NETWORK_FAILED:
+ rc = IOT_OTA_ReportProgress(handle->mqtt_client, buf, buf_len, IOT_OTA_REPORT_TYPE_DOWNLOAD_TIMEOUT, 0,
+ handle->download_now.version);
+ break;
+ case UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED:
+ case UTILS_DOWNLOADER_STATUS_DATA_DOWNLOAD_FAILED:
+ rc = IOT_OTA_ReportProgress(handle->mqtt_client, buf, buf_len, IOT_OTA_REPORT_TYPE_UPGRADE_FAIL, 0,
+ handle->download_now.version);
+ break;
+ default:
+ break;
+ }
+ handle->status = OTA_DOWNLOADER_STATUS_FINISHED;
+ return rc;
+}
+
+// ----------------------------------------------------------------------------
+// API
+// ----------------------------------------------------------------------------
+
+/**
+ * @brief Init ota downloader.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return 0 for success.
+ */
+int ota_downloader_init(void* client)
+{
+ // downloader init
+ UtilsDownloaderFunction ota_callback = {
+ .downloader_malloc = HAL_Malloc,
+ .downloader_free = HAL_Free,
+ // break point
+ .break_point_init = _ota_break_point_init,
+ .break_point_deinit = _ota_break_point_deinit,
+ .break_point_set = _ota_break_point_set,
+ .break_point_save = _ota_break_point_save,
+ .break_point_check = _ota_break_point_check,
+ .break_point_restore = _ota_break_point_restore,
+
+ // data download
+ .data_download_init = _ota_data_download_init,
+ .data_download_deinit = _ota_data_download_deinit,
+ .data_download_is_over = _ota_data_download_is_over,
+ .data_download_recv = _ota_data_download_recv,
+ .data_download_save = _ota_data_download_save,
+ .data_download_finish = _ota_data_download_finish,
+ };
+
+ sg_ota_downloader_handle.downloader = utils_downloader_init(ota_callback, &sg_ota_downloader_handle);
+ if (!sg_ota_downloader_handle.downloader) {
+ Log_e("initialize downloaded failed");
+ return -1;
+ }
+ sg_ota_downloader_handle.mqtt_client = client;
+ sg_ota_downloader_handle.status = OTA_DOWNLOADER_STATUS_INITTED;
+ return 0;
+}
+
+/**
+ * @brief Set download info of ota firmware.
+ *
+ * @param[in] firmware_info pointer to firmware info
+ * @param[in] url url of cos download
+ * @param[in] url_len download length
+ */
+void ota_downloader_info_set(OTAFirmwareInfo* firmware_info, const char* url, int url_len)
+{
+ if (OTA_DOWNLOADER_STATUS_DOWNLOADING != sg_ota_downloader_handle.status) {
+ memcpy(&sg_ota_downloader_handle.download_now, firmware_info, sizeof(OTAFirmwareInfo));
+ strncpy(sg_ota_downloader_handle.download_url, url, url_len);
+ sg_ota_downloader_handle.download_url[url_len] = '\0';
+
+ sg_ota_downloader_handle.status = OTA_DOWNLOADER_STATUS_DOWNLOADING;
+ }
+}
+
+/**
+ * @brief Process ota download.
+ *
+ * @return @see OTADownloaderStatus
+ */
+OTADownloaderStatus ota_downloader_process(void)
+{
+ if (OTA_DOWNLOADER_STATUS_DOWNLOADING == sg_ota_downloader_handle.status) {
+ utils_downloader_process(sg_ota_downloader_handle.downloader);
+ }
+ return sg_ota_downloader_handle.status;
+}
+
+/**
+ * @brief Deinit ota downloader.
+ *
+ */
+void ota_downloader_deinit(void)
+{
+ utils_downloader_deinit(sg_ota_downloader_handle.downloader);
+ memset(&sg_ota_downloader_handle, 0, sizeof(sg_ota_downloader_handle));
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/app/ota/ota_downloader.h b/components/connectivity/iot-hub-device-c-sdk/app/ota/ota_downloader.h
new file mode 100644
index 00000000..1c62b43e
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/app/ota/ota_downloader.h
@@ -0,0 +1,105 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file ota_downloader.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-10-20
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-10-20 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_APP_OTA_OTA_DOWNLOADER_H_
+#define IOT_HUB_DEVICE_C_SDK_APP_OTA_OTA_DOWNLOADER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+#include "qcloud_iot_common.h"
+#include "utils_downloader.h"
+
+#define OTA_FILE_PATH "./app_ota_fw.bin"
+#define OTA_BREAK_POINT_FILE_PATH "./break_point.dat"
+
+/**
+ * @brief Firmware version size.
+ *
+ */
+#define MAX_SIZE_OF_FW_VERSION 32
+
+/**
+ * @brief OTA firmware info.
+ *
+ */
+typedef struct {
+ char version[MAX_SIZE_OF_FW_VERSION + 4];
+ uint32_t file_size;
+ char md5sum[33];
+} OTAFirmwareInfo;
+
+/**
+ * @brief Downloader status.
+ *
+ */
+typedef enum {
+ OTA_DOWNLOADER_STATUS_NO_INITTED = 0,
+ OTA_DOWNLOADER_STATUS_INITTED,
+ OTA_DOWNLOADER_STATUS_DOWNLOADING,
+ OTA_DOWNLOADER_STATUS_FINISHED,
+} OTADownloaderStatus;
+
+/**
+ * @brief Init ota downloader.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return 0 for success.
+ */
+int ota_downloader_init(void* client);
+
+/**
+ * @brief Set download info of ota firmware.
+ *
+ * @param[in] firmware_info pointer to firmware info
+ * @param[in] url url of cos download
+ * @param[in] url_len download length
+ */
+void ota_downloader_info_set(OTAFirmwareInfo* firmware_info, const char* url, int url_len);
+
+/**
+ * @brief Process ota download.
+ *
+ * @return @see OTADownloaderStatus
+ */
+OTADownloaderStatus ota_downloader_process(void);
+
+/**
+ * @brief Deinit ota downloader.
+ *
+ */
+void ota_downloader_deinit(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_APP_OTA_OTA_DOWNLOADER_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/cmake_build.sh b/components/connectivity/iot-hub-device-c-sdk/cmake_build.sh
new file mode 100644
index 00000000..2f05a8eb
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/cmake_build.sh
@@ -0,0 +1,7 @@
+rm -rf build
+git submodule init
+git submodule update
+mkdir -p build
+cd build
+cmake ..
+make all -j16
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/cryptology/CMakeLists.txt b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/CMakeLists.txt
new file mode 100644
index 00000000..c46641a6
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/CMakeLists.txt
@@ -0,0 +1,10 @@
+file(GLOB src ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
+set(inc ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
+
+set(src_common ${src_common} ${src} PARENT_SCOPE)
+set(inc_common ${inc_common} ${inc} PARENT_SCOPE)
+
+if( ${CONFIG_IOT_TEST} STREQUAL "ON")
+ file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
+ set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
+endif()
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/cryptology/inc/utils_base64.h b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/inc/utils_base64.h
new file mode 100644
index 00000000..f78ae71d
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/inc/utils_base64.h
@@ -0,0 +1,82 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file utils_base64.h
+ * @brief utils base64 header file
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-28
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-28 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_BASE64_H_
+#define IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_BASE64_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#include
+
+/**
+ * @brief Encode a buffer into base64 format.
+ *
+ * @param[out] dst destination buffer
+ * @param[in] dlen size of the destination buffer
+ * @param[out] olen number of bytes written
+ * @param[in] src source buffer
+ * @param[in] slen amount of data to be encoded
+ * @return 0 if successful, or MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL.
+ * *olen is always updated to reflect the amount
+ * of data that has (or would have) been written.
+ * If that length cannot be represented, then no data is
+ * written to the buffer and *olen is set to the maximum
+ * length representable as a size_t.
+ *
+ * @note Call this function with dlen = 0 to obtain the
+ * required buffer size in *olen
+ */
+int utils_base64encode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen);
+
+/**
+ * @brief Decode a base64-formatted buffer.
+ *
+ * @param[out] dst destination buffer (can be NULL for checking size)
+ * @param[in] dlen size of the destination buffer
+ * @param[out] olen number of bytes written
+ * @param[in] src source buffer
+ * @param[in] slen amount of data to be decoded
+ * @return 0 if successful, MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL, or
+ * MBEDTLS_ERR_BASE64_INVALID_CHARACTER if the input data is
+ * not correct. *olen is always updated to reflect the amount
+ * of data that has (or would have) been written.
+ *
+ * @note Call this function with *dst = NULL or dlen = 0 to obtain
+ * the required buffer size in *olen
+ */
+int utils_base64decode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_BASE64_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/cryptology/inc/utils_hmac.h b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/inc/utils_hmac.h
new file mode 100644
index 00000000..4db388ab
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/inc/utils_hmac.h
@@ -0,0 +1,74 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file utils_hmac.h
+ * @brief header file for utils_hmac
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-31
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-31 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_HMAC_H_
+#define IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_HMAC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+#include "utils_sha1.h"
+
+/**
+ * @brief Sha1 digest size.
+ *
+ */
+#define SHA1_DIGEST_SIZE 20
+
+/**
+ * @brief Get digest of hmac-sha1.
+ *
+ * @param[in] msg message to hmac-sha1
+ * @param[in] msg_len message len
+ * @param[in] key key using in hmac-sha1
+ * @param[in] key_len key len
+ * @param[out] digest digest to calculate
+ * @return 0 for success
+ */
+int utils_hmac_sha1(const char *msg, int msg_len, const uint8_t *key, int key_len, char *digest);
+
+/**
+ * @brief Get digest hex of hmac-sha1. Output hexstr format.
+ *
+ * @param[in] msg message to hmac-sha1
+ * @param[in] msg_len message len
+ * @param[in] key key using in hmac-sha1
+ * @param[in] key_len key len
+ * @param[out] digest digest to calculate
+ * @return 0 for success
+ */
+int utils_hmac_sha1_hex(const char *msg, int msg_len, const uint8_t *key, int key_len, char *digest);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_HMAC_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/cryptology/inc/utils_md5.h b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/inc/utils_md5.h
new file mode 100644
index 00000000..94a43913
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/inc/utils_md5.h
@@ -0,0 +1,94 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file utils_md5.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-10-20
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-10-20 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_MD5_H_
+#define IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_MD5_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#include
+#include "qcloud_iot_config.h"
+
+#ifdef AUTH_WITH_NO_TLS
+
+typedef struct {
+ uint32_t total[2]; /**< number of bytes processed */
+ uint32_t state[4]; /**< intermediate digest state */
+ uint8_t buffer[64]; /**< data block being processed */
+ char md5sum[33]; /**< md5sum result in hex string */
+} IotMd5Context;
+
+#else
+#include "mbedtls/md5.h"
+typedef struct {
+ mbedtls_md5_context ctx;
+ char md5sum[33]; /**< md5sum result in hex string */
+} IotMd5Context;
+#endif
+
+/**
+ * @brief Reset MD5 context.
+ *
+ * @param[in,out] ctx MD5 context
+ */
+void utils_md5_reset(IotMd5Context *ctx);
+
+/**
+ * @brief MD5 update.
+ *
+ * @param[in,out] ctx MD5 context
+ * @param[in] input input data
+ * @param[in] ilen data length
+ */
+void utils_md5_update(IotMd5Context *ctx, const uint8_t *input, size_t ilen);
+
+/**
+ * @brief Finish MD5 calculation, result will store in md5sum.
+ *
+ * @param[in,out] ctx MD5 context
+ */
+void utils_md5_finish(IotMd5Context *ctx);
+
+/**
+ * @brief Compare md5sum with context.
+ *
+ * @param[in,out] ctx MD5 context
+ * @param[in] md5sum md5sum to compare
+ * @return 0 for the same
+ */
+int utils_md5_compare(IotMd5Context *ctx, const char md5sum[33]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_MD5_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/cryptology/inc/utils_sha1.h b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/inc/utils_sha1.h
new file mode 100644
index 00000000..6bdc61ec
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/inc/utils_sha1.h
@@ -0,0 +1,119 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file utils_sha1.h
+ * @brief header file for utils-sha1
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-31
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-31 1.0 fancyxu first commit
+ * 2021-07-08 1.1 fancyxu fix code standard of IotSha1Context
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_SHA1_H_
+#define IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_SHA1_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#include
+
+/**
+ * @brief SHA-1 context structure.
+ *
+ */
+typedef struct {
+ uint32_t total[2]; /**< number of bytes processed */
+ uint32_t state[5]; /**< intermediate digest state */
+ unsigned char buffer[64]; /**< data block being processed */
+} IotSha1Context;
+
+/**
+ * @brief Initialize SHA-1 context.
+ *
+ * @param[in,out] ctx SHA-1 context to be initialized
+ */
+void utils_sha1_init(IotSha1Context *ctx);
+
+/**
+ * @brief Clear SHA-1 context.
+ *
+ * @param[in,out] ctx SHA-1 context to be cleared
+ */
+void utils_sha1_free(IotSha1Context *ctx);
+
+/**
+ * @brief Clone (the state of) a SHA-1 context.
+ *
+ * @param[out] dst The destination context
+ * @param[in] src The context to be cloned
+ */
+void utils_sha1_clone(IotSha1Context *dst, const IotSha1Context *src);
+
+/**
+ * @brief SHA-1 context setup
+ *
+ * @param[in,out] ctx context to be initialized
+ */
+void utils_sha1_starts(IotSha1Context *ctx);
+
+/**
+ * @brief SHA-1 process buffer.
+ *
+ * @param[in,out] ctx SHA-1 context
+ * @param[in] input buffer holding the data
+ * @param[in] ilen length of the input data
+ */
+void utils_sha1_update(IotSha1Context *ctx, const unsigned char *input, size_t ilen);
+
+/**
+ * @brief SHA-1 final digest
+ *
+ * @param[in,out] ctx SHA-1 context
+ * @param[out] output SHA-1 checksum result
+ */
+void utils_sha1_finish(IotSha1Context *ctx, unsigned char output[20]);
+
+/**
+ * @brief Output = SHA-1( input buffer )
+ *
+ * @param[in] input buffer holding the data
+ * @param[in] ilen length of the input data
+ * @param[out] output SHA-1 checksum result
+ */
+void utils_sha1(const unsigned char *input, size_t ilen, unsigned char output[20]);
+
+/**
+ * @brief Output = SHA-1( input buffer )
+ *
+ * @param[in] input buffer holding the data
+ * @param[in] ilen length of the input data
+ * @param[out] output SHA-1 checksum result hex
+ */
+void utils_sha1_hex(const unsigned char *input, size_t ilen, unsigned char output_hex[40]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_SHA1_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/cryptology/src/utils_base64.c b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/src/utils_base64.c
new file mode 100644
index 00000000..6293d747
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/src/utils_base64.c
@@ -0,0 +1,240 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file utils_base64.c
+ * @brief base64 encdoe & decode. Reference mbedtls/base64.c.
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-27
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-27 1.0 fancyxu first commit
+ *
+ */
+
+#include "utils_base64.h"
+
+static const unsigned char base64_enc_map[64] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+ 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
+ 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
+
+static const unsigned char base64_dec_map[128] = {
+ 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 62,
+ 127, 127, 127, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 127, 127, 127, 64, 127, 127, 127, 0,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 127, 127, 127, 127, 127, 127, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 127, 127, 127, 127, 127};
+
+#define BASE64_SIZE_T_MAX ((size_t)-1) /* SIZE_T_MAX is not standard */
+
+/**
+ * @brief Check if src buf and dst buf.
+ *
+ * @param[in] dst destination buffer
+ * @param[in] dlen size of the destination buffer
+ * @param[in] src source buffer
+ * @param[in] slen amount of data to be encoded
+ * @return 0 for valid, -1 for invalid
+ */
+static int _utils_base64_check_valid(unsigned char *dst, size_t dlen, const unsigned char *src, size_t slen)
+{
+ size_t i, n;
+ uint32_t j, x;
+
+ /* First pass: check for validity and get output length */
+ for (i = n = j = 0; i < slen; i++) {
+ /* Skip spaces before checking for EOL */
+ x = 0;
+ while (i < slen && src[i] == ' ') {
+ ++i;
+ ++x;
+ }
+
+ /* Spaces at end of buffer are OK */
+ if (i == slen)
+ break;
+
+ if ((slen - i) >= 2 && src[i] == '\r' && src[i + 1] == '\n')
+ continue;
+
+ if (src[i] == '\n')
+ continue;
+
+ /* Space inside a line is an error */
+ if (x != 0)
+ return -1;
+
+ if (src[i] == '=' && ++j > 2)
+ return -1;
+
+ if (src[i] > 127 || base64_dec_map[src[i]] == 127)
+ return -1;
+
+ if (base64_dec_map[src[i]] < 64 && j != 0)
+ return -1;
+
+ n++;
+ }
+
+ // if no useful data, means error
+ if (n == 0) {
+ return -1;
+ }
+
+ /* The following expression is to calculate the following formula without
+ * risk of integer overflow in n:
+ * n = ( ( n * 6 ) + 7 ) >> 3;
+ */
+ n = (6 * (n >> 3)) + ((6 * (n & 0x7) + 7) >> 3);
+ n -= j;
+
+ if (!dst || dlen < n) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Encode a buffer into base64 format.
+ *
+ * @param[out] dst destination buffer
+ * @param[in] dlen size of the destination buffer
+ * @param[out] olen number of bytes written
+ * @param[in] src source buffer
+ * @param[in] slen amount of data to be encoded
+ * @return 0 if successful, or MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL.
+ * *olen is always updated to reflect the amount
+ * of data that has (or would have) been written.
+ * If that length cannot be represented, then no data is
+ * written to the buffer and *olen is set to the maximum
+ * length representable as a size_t.
+ *
+ * @note Call this function with dlen = 0 to obtain the
+ * required buffer size in *olen
+ */
+int utils_base64encode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen)
+{
+ size_t i, n;
+ unsigned char *p;
+
+ if (slen == 0) {
+ *olen = 0;
+ return 0;
+ }
+
+ n = slen / 3 + (slen % 3 != 0);
+
+ if (n > (BASE64_SIZE_T_MAX - 1) / 4) {
+ *olen = BASE64_SIZE_T_MAX;
+ return -1;
+ }
+
+ n *= 4;
+
+ if ((dlen < n + 1) || (NULL == dst)) {
+ *olen = n + 1;
+ return -1;
+ }
+
+ n = (slen / 3) * 3;
+
+ int C1, C2, C3;
+ for (i = 0, p = dst; i < n; i += 3) {
+ C1 = *src++;
+ C2 = *src++;
+ C3 = *src++;
+
+ *p++ = base64_enc_map[(C1 >> 2) & 0x3F];
+ *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F];
+ *p++ = base64_enc_map[(((C2 & 15) << 2) + (C3 >> 6)) & 0x3F];
+ *p++ = base64_enc_map[C3 & 0x3F];
+ }
+
+ if (i < slen) {
+ C1 = *src++;
+ C2 = ((i + 1) < slen) ? *src++ : 0;
+
+ *p++ = base64_enc_map[(C1 >> 2) & 0x3F];
+ *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F];
+
+ if ((i + 1) < slen)
+ *p++ = base64_enc_map[((C2 & 15) << 2) & 0x3F];
+ else
+ *p++ = '=';
+
+ *p++ = '=';
+ }
+
+ *olen = p - dst;
+ *p = 0;
+
+ return 0;
+}
+
+/**
+ * @brief Decode a base64-formatted buffer.
+ *
+ * @param[out] dst destination buffer (can be NULL for checking size)
+ * @param[in] dlen size of the destination buffer
+ * @param[out] olen number of bytes written
+ * @param[in] src source buffer
+ * @param[in] slen amount of data to be decoded
+ * @return 0 if successful, MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL, or
+ * MBEDTLS_ERR_BASE64_INVALID_CHARACTER if the input data is
+ * not correct. *olen is always updated to reflect the amount
+ * of data that has (or would have) been written.
+ *
+ * @note Call this function with *dst = NULL or dlen = 0 to obtain
+ * the required buffer size in *olen
+ */
+int utils_base64decode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen)
+{
+ size_t i, n;
+ uint32_t j, x;
+ unsigned char *p;
+
+ /* First pass: check for validity and get output length */
+ if (_utils_base64_check_valid(dst, dlen, src, slen)) {
+ *olen = 0;
+ return -1;
+ }
+
+ for (i = slen, j = 3, n = x = 0, p = dst; i > 0; i--, src++) {
+ if (*src == '\r' || *src == '\n' || *src == ' ')
+ continue;
+
+ j -= (base64_dec_map[*src] == 64);
+ x = (x << 6) | (base64_dec_map[*src] & 0x3F);
+
+ if (++n == 4) {
+ n = 0;
+ if (j > 0)
+ *p++ = (unsigned char)(x >> 16);
+ if (j > 1)
+ *p++ = (unsigned char)(x >> 8);
+ if (j > 2)
+ *p++ = (unsigned char)(x);
+ }
+ }
+
+ *olen = p - dst;
+
+ return 0;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/cryptology/src/utils_hmac.c b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/src/utils_hmac.c
new file mode 100644
index 00000000..025ca03f
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/src/utils_hmac.c
@@ -0,0 +1,144 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file utils_hmac.c
+ * @brief Support hmac
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-31
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-31 1.0 fancyxu first commit
+ * 2021-07-08 1.1 fancyxu fix code standard of IotSha1Context
+ *
+ */
+
+#include "utils_hmac.h"
+
+/**
+ * @brief inner/outer padding size
+ *
+ */
+#define KEY_IO_PAD_SIZE 64
+
+/**
+ * @brief sha1 digest size
+ *
+ */
+#define SHA1_DIGEST_SIZE 20
+
+/**
+ * @brief Binary half byte to hex char
+ *
+ * @param[in] hb half byte
+ * @return hex char(0~f)
+ */
+static char _hb2hex(uint8_t hb)
+{
+ hb = hb & 0xF;
+ return (char)(hb < 10 ? '0' + hb : hb - 10 + 'a');
+}
+
+static int _utils_hmac_sha1_process(const char *msg, int msg_len, const uint8_t *key, int key_len, unsigned char *out)
+{
+ IotSha1Context context;
+ unsigned char k_ipad[KEY_IO_PAD_SIZE]; /* inner padding - key XORd with ipad */
+ unsigned char k_opad[KEY_IO_PAD_SIZE]; /* outer padding - key XORd with opad */
+
+ int i;
+
+ /* start out by storing key in pads */
+ memset(k_ipad, 0, sizeof(k_ipad));
+ memset(k_opad, 0, sizeof(k_opad));
+ memcpy(k_ipad, key, key_len);
+ memcpy(k_opad, key, key_len);
+
+ /* XOR key with ipad and opad values */
+ for (i = 0; i < KEY_IO_PAD_SIZE; i++) {
+ k_ipad[i] ^= 0x36;
+ k_opad[i] ^= 0x5c;
+ }
+
+ /* perform inner SHA */
+ utils_sha1_init(&context); /* init context for 1st pass */
+ utils_sha1_starts(&context); /* setup context for 1st pass */
+ utils_sha1_update(&context, k_ipad, KEY_IO_PAD_SIZE); /* start with inner pad */
+ utils_sha1_update(&context, (unsigned char *)msg, msg_len); /* then text of datagram */
+ utils_sha1_finish(&context, out); /* finish up 1st pass */
+
+ /* perform outer SHA */
+ utils_sha1_init(&context); /* init context for 2nd pass */
+ utils_sha1_starts(&context); /* setup context for 2nd pass */
+ utils_sha1_update(&context, k_opad, KEY_IO_PAD_SIZE); /* start with outer pad */
+ utils_sha1_update(&context, out, SHA1_DIGEST_SIZE); /* then results of 1st hash */
+ utils_sha1_finish(&context, out); /* finish up 2nd pass */
+ return 0;
+}
+
+/**
+ * @brief Get digest of hmac-sha1.
+ *
+ * @param[in] msg message to hmac-sha1
+ * @param[in] msg_len message len
+ * @param[in] key key using in hmac-sha1
+ * @param[in] key_len key len
+ * @param[out] digest digest to calculate
+ * @return 0 for success
+ */
+int utils_hmac_sha1(const char *msg, int msg_len, const uint8_t *key, int key_len, char *digest)
+{
+ unsigned char out[SHA1_DIGEST_SIZE] = {0};
+ if (!msg || !digest || !key) {
+ return -1;
+ }
+
+ if (key_len > KEY_IO_PAD_SIZE) {
+ return -1;
+ }
+
+ _utils_hmac_sha1_process(msg, msg_len, key, key_len, out);
+ memcpy(digest, out, SHA1_DIGEST_SIZE);
+ return 0;
+}
+
+/**
+ * @brief Get digest hex of hmac-sha1.
+ *
+ * @param[in] msg message to hmac-sha1
+ * @param[in] msg_len message len
+ * @param[in] key key using in hmac-sha1
+ * @param[in] key_len key len
+ * @param[out] digest digest to calculate
+ * @return 0 for success
+ */
+int utils_hmac_sha1_hex(const char *msg, int msg_len, const uint8_t *key, int key_len, char *digest)
+{
+ unsigned char out[SHA1_DIGEST_SIZE] = {0};
+ if (!msg || !digest || !key) {
+ return -1;
+ }
+
+ if (key_len > KEY_IO_PAD_SIZE) {
+ return -1;
+ }
+ _utils_hmac_sha1_process(msg, msg_len, key, key_len, out);
+ for (int i = 0; i < SHA1_DIGEST_SIZE; ++i) {
+ digest[i * 2] = _hb2hex(out[i] >> 4);
+ digest[i * 2 + 1] = _hb2hex(out[i]);
+ }
+ return 0;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/cryptology/src/utils_md5.c b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/src/utils_md5.c
new file mode 100644
index 00000000..4607d581
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/src/utils_md5.c
@@ -0,0 +1,409 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file utils_md5.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-10-27
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-10-27 1.0 fancyxu first commit
+ *
+ */
+
+#include "utils_md5.h"
+
+/**
+ * @brief Binary half byte to hex char
+ *
+ * @param[in] hb half byte
+ * @return hex char(0~f)
+ */
+static char _hb2hex(uint8_t hb)
+{
+ hb = hb & 0xF;
+ return (char)(hb < 10 ? '0' + hb : hb - 10 + 'a');
+}
+
+/**
+ * @brief Lower md5sum.
+ *
+ * @param[out] md5_lower md5 sum lower
+ * @param[in] md5 md5 sum
+ */
+static void _lower(char md5_lower[33], const char md5[33])
+{
+ int i = 0;
+ for (i = 0; i < 32; i++) {
+ md5_lower[i] = md5[i];
+ if (md5[i] >= 'A' && md5[i] <= 'F') {
+ md5_lower[i] += ('a' - 'A');
+ }
+ }
+ md5_lower[32] = '\0';
+}
+
+#ifdef AUTH_WITH_NO_TLS
+/**
+ * @brief 32-bit integer manipulation macros (little endian)
+ *
+ */
+#define IOT_MD5_GET_UINT32_LE(n, b, i) \
+ { \
+ (n) = ((uint32_t)(b)[(i)]) | ((uint32_t)(b)[(i) + 1] << 8) | ((uint32_t)(b)[(i) + 2] << 16) | \
+ ((uint32_t)(b)[(i) + 3] << 24); \
+ }
+
+#define IOT_MD5_PUT_UINT32_LE(n, b, i) \
+ { \
+ (b)[(i)] = (uint8_t)(((n)) & 0xFF); \
+ (b)[(i) + 1] = (uint8_t)(((n) >> 8) & 0xFF); \
+ (b)[(i) + 2] = (uint8_t)(((n) >> 16) & 0xFF); \
+ (b)[(i) + 3] = (uint8_t)(((n) >> 24) & 0xFF); \
+ }
+
+/**
+ * @brief Padding for md5.
+ *
+ */
+static const uint8_t sg_iot_md5_padding[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+/**
+ * @brief Initialize md5 context.
+ *
+ * @param[in,out] ctx MD5 context
+ */
+static void _utils_md5_init(IotMd5Context *ctx)
+{
+ memset(ctx, 0, sizeof(IotMd5Context));
+}
+
+/**
+ * @brief Start md5 calculate.
+ *
+ * @param[in,out] ctx MD5 context
+ */
+static void _utils_md5_start(IotMd5Context *ctx)
+{
+ ctx->total[0] = 0;
+ ctx->total[1] = 0;
+
+ ctx->state[0] = 0x67452301;
+ ctx->state[1] = 0xEFCDAB89;
+ ctx->state[2] = 0x98BADCFE;
+ ctx->state[3] = 0x10325476;
+}
+
+/**
+ * @brief Calculate md5.
+ *
+ * @param[in,out] ctx MD5 context
+ * @param[in] data data to calculate
+ */
+static void _utils_md5_process(IotMd5Context *ctx, const uint8_t data[64])
+{
+ uint32_t X[16], A, B, C, D;
+
+ IOT_MD5_GET_UINT32_LE(X[0], data, 0);
+ IOT_MD5_GET_UINT32_LE(X[1], data, 4);
+ IOT_MD5_GET_UINT32_LE(X[2], data, 8);
+ IOT_MD5_GET_UINT32_LE(X[3], data, 12);
+ IOT_MD5_GET_UINT32_LE(X[4], data, 16);
+ IOT_MD5_GET_UINT32_LE(X[5], data, 20);
+ IOT_MD5_GET_UINT32_LE(X[6], data, 24);
+ IOT_MD5_GET_UINT32_LE(X[7], data, 28);
+ IOT_MD5_GET_UINT32_LE(X[8], data, 32);
+ IOT_MD5_GET_UINT32_LE(X[9], data, 36);
+ IOT_MD5_GET_UINT32_LE(X[10], data, 40);
+ IOT_MD5_GET_UINT32_LE(X[11], data, 44);
+ IOT_MD5_GET_UINT32_LE(X[12], data, 48);
+ IOT_MD5_GET_UINT32_LE(X[13], data, 52);
+ IOT_MD5_GET_UINT32_LE(X[14], data, 56);
+ IOT_MD5_GET_UINT32_LE(X[15], data, 60);
+
+#define S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+
+#define P(a, b, c, d, k, s, t) \
+ { \
+ a += F(b, c, d) + X[k] + t; \
+ a = S(a, s) + b; \
+ }
+
+ A = ctx->state[0];
+ B = ctx->state[1];
+ C = ctx->state[2];
+ D = ctx->state[3];
+
+#define F(x, y, z) (z ^ (x & (y ^ z)))
+
+ P(A, B, C, D, 0, 7, 0xD76AA478);
+ P(D, A, B, C, 1, 12, 0xE8C7B756);
+ P(C, D, A, B, 2, 17, 0x242070DB);
+ P(B, C, D, A, 3, 22, 0xC1BDCEEE);
+ P(A, B, C, D, 4, 7, 0xF57C0FAF);
+ P(D, A, B, C, 5, 12, 0x4787C62A);
+ P(C, D, A, B, 6, 17, 0xA8304613);
+ P(B, C, D, A, 7, 22, 0xFD469501);
+ P(A, B, C, D, 8, 7, 0x698098D8);
+ P(D, A, B, C, 9, 12, 0x8B44F7AF);
+ P(C, D, A, B, 10, 17, 0xFFFF5BB1);
+ P(B, C, D, A, 11, 22, 0x895CD7BE);
+ P(A, B, C, D, 12, 7, 0x6B901122);
+ P(D, A, B, C, 13, 12, 0xFD987193);
+ P(C, D, A, B, 14, 17, 0xA679438E);
+ P(B, C, D, A, 15, 22, 0x49B40821);
+
+#undef F
+
+#define F(x, y, z) (y ^ (z & (x ^ y)))
+
+ P(A, B, C, D, 1, 5, 0xF61E2562);
+ P(D, A, B, C, 6, 9, 0xC040B340);
+ P(C, D, A, B, 11, 14, 0x265E5A51);
+ P(B, C, D, A, 0, 20, 0xE9B6C7AA);
+ P(A, B, C, D, 5, 5, 0xD62F105D);
+ P(D, A, B, C, 10, 9, 0x02441453);
+ P(C, D, A, B, 15, 14, 0xD8A1E681);
+ P(B, C, D, A, 4, 20, 0xE7D3FBC8);
+ P(A, B, C, D, 9, 5, 0x21E1CDE6);
+ P(D, A, B, C, 14, 9, 0xC33707D6);
+ P(C, D, A, B, 3, 14, 0xF4D50D87);
+ P(B, C, D, A, 8, 20, 0x455A14ED);
+ P(A, B, C, D, 13, 5, 0xA9E3E905);
+ P(D, A, B, C, 2, 9, 0xFCEFA3F8);
+ P(C, D, A, B, 7, 14, 0x676F02D9);
+ P(B, C, D, A, 12, 20, 0x8D2A4C8A);
+
+#undef F
+
+#define F(x, y, z) (x ^ y ^ z)
+
+ P(A, B, C, D, 5, 4, 0xFFFA3942);
+ P(D, A, B, C, 8, 11, 0x8771F681);
+ P(C, D, A, B, 11, 16, 0x6D9D6122);
+ P(B, C, D, A, 14, 23, 0xFDE5380C);
+ P(A, B, C, D, 1, 4, 0xA4BEEA44);
+ P(D, A, B, C, 4, 11, 0x4BDECFA9);
+ P(C, D, A, B, 7, 16, 0xF6BB4B60);
+ P(B, C, D, A, 10, 23, 0xBEBFBC70);
+ P(A, B, C, D, 13, 4, 0x289B7EC6);
+ P(D, A, B, C, 0, 11, 0xEAA127FA);
+ P(C, D, A, B, 3, 16, 0xD4EF3085);
+ P(B, C, D, A, 6, 23, 0x04881D05);
+ P(A, B, C, D, 9, 4, 0xD9D4D039);
+ P(D, A, B, C, 12, 11, 0xE6DB99E5);
+ P(C, D, A, B, 15, 16, 0x1FA27CF8);
+ P(B, C, D, A, 2, 23, 0xC4AC5665);
+
+#undef F
+
+#define F(x, y, z) (y ^ (x | ~z))
+
+ P(A, B, C, D, 0, 6, 0xF4292244);
+ P(D, A, B, C, 7, 10, 0x432AFF97);
+ P(C, D, A, B, 14, 15, 0xAB9423A7);
+ P(B, C, D, A, 5, 21, 0xFC93A039);
+ P(A, B, C, D, 12, 6, 0x655B59C3);
+ P(D, A, B, C, 3, 10, 0x8F0CCC92);
+ P(C, D, A, B, 10, 15, 0xFFEFF47D);
+ P(B, C, D, A, 1, 21, 0x85845DD1);
+ P(A, B, C, D, 8, 6, 0x6FA87E4F);
+ P(D, A, B, C, 15, 10, 0xFE2CE6E0);
+ P(C, D, A, B, 6, 15, 0xA3014314);
+ P(B, C, D, A, 13, 21, 0x4E0811A1);
+ P(A, B, C, D, 4, 6, 0xF7537E82);
+ P(D, A, B, C, 11, 10, 0xBD3AF235);
+ P(C, D, A, B, 2, 15, 0x2AD7D2BB);
+ P(B, C, D, A, 9, 21, 0xEB86D391);
+
+#undef F
+
+ ctx->state[0] += A;
+ ctx->state[1] += B;
+ ctx->state[2] += C;
+ ctx->state[3] += D;
+}
+
+/**
+ * @brief Reset MD5 context.
+ *
+ * @param[in,out] ctx MD5 context
+ */
+void utils_md5_reset(IotMd5Context *ctx)
+{
+ memset(ctx, 0, sizeof(IotMd5Context));
+ _utils_md5_init(ctx);
+ _utils_md5_start(ctx);
+}
+
+/**
+ * @brief MD5 update.
+ *
+ * @param[in,out] ctx MD5 context
+ * @param[in] input input data
+ * @param[in] ilen data length
+ */
+void utils_md5_update(IotMd5Context *ctx, const uint8_t *input, size_t ilen)
+{
+ size_t fill;
+ uint32_t left;
+
+ if (ilen == 0) {
+ return;
+ }
+
+ left = ctx->total[0] & 0x3F;
+ fill = 64 - left;
+
+ ctx->total[0] += (uint32_t)ilen;
+ ctx->total[0] &= 0xFFFFFFFF;
+
+ if (ctx->total[0] < (uint32_t)ilen) {
+ ctx->total[1]++;
+ }
+
+ if (left && ilen >= fill) {
+ memcpy((void *)(ctx->buffer + left), input, fill);
+ _utils_md5_process(ctx, ctx->buffer);
+ input += fill;
+ ilen -= fill;
+ left = 0;
+ }
+
+ while (ilen >= 64) {
+ _utils_md5_process(ctx, input);
+ input += 64;
+ ilen -= 64;
+ }
+
+ if (ilen > 0) {
+ memcpy((void *)(ctx->buffer + left), input, ilen);
+ }
+}
+
+/**
+ * @brief Finish MD5 calculation, result will store in md5sum.
+ *
+ * @param[in,out] ctx MD5 context
+ */
+void utils_md5_finish(IotMd5Context *ctx)
+{
+ int i;
+ uint32_t last, padn;
+ uint32_t high, low;
+ uint8_t msglen[8];
+
+ uint8_t output_tmp[16];
+
+ high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
+ low = (ctx->total[0] << 3);
+
+ IOT_MD5_PUT_UINT32_LE(low, msglen, 0);
+ IOT_MD5_PUT_UINT32_LE(high, msglen, 4);
+
+ last = ctx->total[0] & 0x3F;
+ padn = (last < 56) ? (56 - last) : (120 - last);
+
+ utils_md5_update(ctx, sg_iot_md5_padding, padn);
+ utils_md5_update(ctx, msglen, 8);
+
+ IOT_MD5_PUT_UINT32_LE(ctx->state[0], output_tmp, 0);
+ IOT_MD5_PUT_UINT32_LE(ctx->state[1], output_tmp, 4);
+ IOT_MD5_PUT_UINT32_LE(ctx->state[2], output_tmp, 8);
+ IOT_MD5_PUT_UINT32_LE(ctx->state[3], output_tmp, 12);
+
+ for (i = 0; i < 16; ++i) {
+ ctx->md5sum[i * 2] = _hb2hex(output_tmp[i] >> 4);
+ ctx->md5sum[i * 2 + 1] = _hb2hex(output_tmp[i]);
+ }
+ ctx->md5sum[32] = '\0';
+}
+
+/**
+ * @brief Compare md5sum with context.
+ *
+ * @param[in,out] ctx MD5 context
+ * @param[in] md5sum md5sum to compare
+ * @return 0 for the same
+ */
+int utils_md5_compare(IotMd5Context *ctx, const char md5sum[33])
+{
+ char md5sum_lower[33];
+ _lower(md5sum_lower, md5sum);
+ return strncmp(ctx->md5sum, md5sum_lower, 32);
+}
+#else
+
+/**
+ * @brief Reset MD5 context.
+ *
+ * @param[in,out] ctx MD5 context
+ */
+void utils_md5_reset(IotMd5Context *ctx)
+{
+ memset(ctx, 0, sizeof(IotMd5Context));
+ mbedtls_md5_init(&ctx->ctx);
+ mbedtls_md5_starts(&ctx->ctx);
+}
+
+/**
+ * @brief MD5 update.
+ *
+ * @param[in,out] ctx MD5 context
+ * @param[in] input input data
+ * @param[in] ilen data length
+ */
+void utils_md5_update(IotMd5Context *ctx, const uint8_t *input, size_t ilen)
+{
+ mbedtls_md5_update(&ctx->ctx, input, ilen);
+}
+
+/**
+ * @brief Finish MD5 calculation, result will store in md5sum.
+ *
+ * @param[in,out] ctx MD5 context
+ */
+void utils_md5_finish(IotMd5Context *ctx)
+{
+ int i;
+ uint8_t output_tmp[16];
+ mbedtls_md5_finish(&ctx->ctx, output_tmp);
+ for (i = 0; i < 16; ++i) {
+ ctx->md5sum[i * 2] = _hb2hex(output_tmp[i] >> 4);
+ ctx->md5sum[i * 2 + 1] = _hb2hex(output_tmp[i]);
+ }
+ ctx->md5sum[32] = '\0';
+}
+
+/**
+ * @brief Compare md5sum with context.
+ *
+ * @param[in,out] ctx MD5 context
+ * @param[in] md5sum md5sum to compare
+ * @return 0 for the same
+ */
+int utils_md5_compare(IotMd5Context *ctx, const char md5sum[33])
+{
+ char md5sum_lower[33];
+ _lower(md5sum_lower, md5sum);
+ return strncmp(ctx->md5sum, md5sum_lower, 32);
+}
+#endif
\ No newline at end of file
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/cryptology/src/utils_sha1.c b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/src/utils_sha1.c
new file mode 100644
index 00000000..96cf6d69
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/src/utils_sha1.c
@@ -0,0 +1,408 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file utils_sha1.c
+ * @brief SHA-1 operation, reference mbedtls
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-31
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-31 1.0 fancyxu first commit
+ * 2021-07-08 1.1 fancyxu fix code standard of IotSha1Context
+ *
+ */
+
+#include "utils_sha1.h"
+
+#ifndef IOT_SHA1_GET_UINT32_BE
+/**
+ * @brief Get 32-bit integer manipulation macros (big endian)
+ *
+ */
+#define IOT_SHA1_GET_UINT32_BE(n, b, i) \
+ { \
+ (n) = ((uint32_t)(b)[(i)] << 24) | ((uint32_t)(b)[(i) + 1] << 16) | ((uint32_t)(b)[(i) + 2] << 8) | \
+ ((uint32_t)(b)[(i) + 3]); \
+ }
+#endif
+
+#ifndef IOT_SHA1_PUT_UINT32_BE
+/**
+ * @brief Put 32-bit integer manipulation macros (big endian)
+ *
+ */
+#define IOT_SHA1_PUT_UINT32_BE(n, b, i) \
+ { \
+ (b)[(i)] = (unsigned char)((n) >> 24); \
+ (b)[(i) + 1] = (unsigned char)((n) >> 16); \
+ (b)[(i) + 2] = (unsigned char)((n) >> 8); \
+ (b)[(i) + 3] = (unsigned char)((n)); \
+ }
+#endif
+
+/**
+ * @brief Binary half byte to hex char
+ *
+ * @param[in] hb half byte
+ * @return hex char(0~f)
+ */
+static char _hb2hex(uint8_t hb)
+{
+ hb = hb & 0xF;
+ return (char)(hb < 10 ? '0' + hb : hb - 10 + 'a');
+}
+
+/**
+ * @brief Implementation that should never be optimized out by the compiler.
+ *
+ * @param[in] v pointer to ctx
+ * @param[in] n sizeof ctx
+ */
+static void utils_sha1_zeroize(void *v, size_t n)
+{
+ volatile unsigned char *p = v;
+ while (n--) {
+ *p++ = 0;
+ }
+}
+
+/**
+ * @brief Initialize SHA-1 context.
+ *
+ * @param[in,out] ctx SHA-1 context to be initialized
+ */
+void utils_sha1_init(IotSha1Context *ctx)
+{
+ memset(ctx, 0, sizeof(IotSha1Context));
+}
+
+/**
+ * @brief Clear SHA-1 context.
+ *
+ * @param[in,out] ctx SHA-1 context to be cleared
+ */
+void utils_sha1_free(IotSha1Context *ctx)
+{
+ if (!ctx) {
+ return;
+ }
+
+ utils_sha1_zeroize(ctx, sizeof(IotSha1Context));
+}
+
+/**
+ * @brief Clone (the state of) a SHA-1 context.
+ *
+ * @param[out] dst The destination context
+ * @param[in] src The context to be cloned
+ */
+void utils_sha1_clone(IotSha1Context *dst, const IotSha1Context *src)
+{
+ *dst = *src;
+}
+
+/**
+ * @brief SHA-1 context setup
+ *
+ * @param[in,out] ctx context to be initialized
+ */
+void utils_sha1_starts(IotSha1Context *ctx)
+{
+ ctx->total[0] = 0;
+ ctx->total[1] = 0;
+
+ ctx->state[0] = 0x67452301;
+ ctx->state[1] = 0xEFCDAB89;
+ ctx->state[2] = 0x98BADCFE;
+ ctx->state[3] = 0x10325476;
+ ctx->state[4] = 0xC3D2E1F0;
+}
+
+/**
+ * @brief SHA-1 process
+ *
+ * @param[in,out] ctx pointer to ctx
+ * @param[in] data data to be calculated
+ */
+void utils_sha1_process(IotSha1Context *ctx, const unsigned char data[64])
+{
+ uint32_t temp, W[16], A, B, C, D, E;
+
+ IOT_SHA1_GET_UINT32_BE(W[0], data, 0);
+ IOT_SHA1_GET_UINT32_BE(W[1], data, 4);
+ IOT_SHA1_GET_UINT32_BE(W[2], data, 8);
+ IOT_SHA1_GET_UINT32_BE(W[3], data, 12);
+ IOT_SHA1_GET_UINT32_BE(W[4], data, 16);
+ IOT_SHA1_GET_UINT32_BE(W[5], data, 20);
+ IOT_SHA1_GET_UINT32_BE(W[6], data, 24);
+ IOT_SHA1_GET_UINT32_BE(W[7], data, 28);
+ IOT_SHA1_GET_UINT32_BE(W[8], data, 32);
+ IOT_SHA1_GET_UINT32_BE(W[9], data, 36);
+ IOT_SHA1_GET_UINT32_BE(W[10], data, 40);
+ IOT_SHA1_GET_UINT32_BE(W[11], data, 44);
+ IOT_SHA1_GET_UINT32_BE(W[12], data, 48);
+ IOT_SHA1_GET_UINT32_BE(W[13], data, 52);
+ IOT_SHA1_GET_UINT32_BE(W[14], data, 56);
+ IOT_SHA1_GET_UINT32_BE(W[15], data, 60);
+
+#define S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+
+#define R(t) \
+ (temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ W[(t - 14) & 0x0F] ^ W[t & 0x0F], (W[t & 0x0F] = S(temp, 1)))
+
+#define P(a, b, c, d, e, x) \
+ { \
+ e += S(a, 5) + F(b, c, d) + K + x; \
+ b = S(b, 30); \
+ }
+
+ A = ctx->state[0];
+ B = ctx->state[1];
+ C = ctx->state[2];
+ D = ctx->state[3];
+ E = ctx->state[4];
+
+#define F(x, y, z) (z ^ (x & (y ^ z)))
+#define K 0x5A827999
+
+ P(A, B, C, D, E, W[0]);
+ P(E, A, B, C, D, W[1]);
+ P(D, E, A, B, C, W[2]);
+ P(C, D, E, A, B, W[3]);
+ P(B, C, D, E, A, W[4]);
+ P(A, B, C, D, E, W[5]);
+ P(E, A, B, C, D, W[6]);
+ P(D, E, A, B, C, W[7]);
+ P(C, D, E, A, B, W[8]);
+ P(B, C, D, E, A, W[9]);
+ P(A, B, C, D, E, W[10]);
+ P(E, A, B, C, D, W[11]);
+ P(D, E, A, B, C, W[12]);
+ P(C, D, E, A, B, W[13]);
+ P(B, C, D, E, A, W[14]);
+ P(A, B, C, D, E, W[15]);
+ P(E, A, B, C, D, R(16));
+ P(D, E, A, B, C, R(17));
+ P(C, D, E, A, B, R(18));
+ P(B, C, D, E, A, R(19));
+
+#undef K
+#undef F
+
+#define F(x, y, z) (x ^ y ^ z)
+#define K 0x6ED9EBA1
+
+ P(A, B, C, D, E, R(20));
+ P(E, A, B, C, D, R(21));
+ P(D, E, A, B, C, R(22));
+ P(C, D, E, A, B, R(23));
+ P(B, C, D, E, A, R(24));
+ P(A, B, C, D, E, R(25));
+ P(E, A, B, C, D, R(26));
+ P(D, E, A, B, C, R(27));
+ P(C, D, E, A, B, R(28));
+ P(B, C, D, E, A, R(29));
+ P(A, B, C, D, E, R(30));
+ P(E, A, B, C, D, R(31));
+ P(D, E, A, B, C, R(32));
+ P(C, D, E, A, B, R(33));
+ P(B, C, D, E, A, R(34));
+ P(A, B, C, D, E, R(35));
+ P(E, A, B, C, D, R(36));
+ P(D, E, A, B, C, R(37));
+ P(C, D, E, A, B, R(38));
+ P(B, C, D, E, A, R(39));
+
+#undef K
+#undef F
+
+#define F(x, y, z) ((x & y) | (z & (x | y)))
+#define K 0x8F1BBCDC
+
+ P(A, B, C, D, E, R(40));
+ P(E, A, B, C, D, R(41));
+ P(D, E, A, B, C, R(42));
+ P(C, D, E, A, B, R(43));
+ P(B, C, D, E, A, R(44));
+ P(A, B, C, D, E, R(45));
+ P(E, A, B, C, D, R(46));
+ P(D, E, A, B, C, R(47));
+ P(C, D, E, A, B, R(48));
+ P(B, C, D, E, A, R(49));
+ P(A, B, C, D, E, R(50));
+ P(E, A, B, C, D, R(51));
+ P(D, E, A, B, C, R(52));
+ P(C, D, E, A, B, R(53));
+ P(B, C, D, E, A, R(54));
+ P(A, B, C, D, E, R(55));
+ P(E, A, B, C, D, R(56));
+ P(D, E, A, B, C, R(57));
+ P(C, D, E, A, B, R(58));
+ P(B, C, D, E, A, R(59));
+
+#undef K
+#undef F
+
+#define F(x, y, z) (x ^ y ^ z)
+#define K 0xCA62C1D6
+
+ P(A, B, C, D, E, R(60));
+ P(E, A, B, C, D, R(61));
+ P(D, E, A, B, C, R(62));
+ P(C, D, E, A, B, R(63));
+ P(B, C, D, E, A, R(64));
+ P(A, B, C, D, E, R(65));
+ P(E, A, B, C, D, R(66));
+ P(D, E, A, B, C, R(67));
+ P(C, D, E, A, B, R(68));
+ P(B, C, D, E, A, R(69));
+ P(A, B, C, D, E, R(70));
+ P(E, A, B, C, D, R(71));
+ P(D, E, A, B, C, R(72));
+ P(C, D, E, A, B, R(73));
+ P(B, C, D, E, A, R(74));
+ P(A, B, C, D, E, R(75));
+ P(E, A, B, C, D, R(76));
+ P(D, E, A, B, C, R(77));
+ P(C, D, E, A, B, R(78));
+ P(B, C, D, E, A, R(79));
+
+#undef K
+#undef F
+
+ ctx->state[0] += A;
+ ctx->state[1] += B;
+ ctx->state[2] += C;
+ ctx->state[3] += D;
+ ctx->state[4] += E;
+}
+
+/**
+ * @brief SHA-1 process buffer.
+ *
+ * @param[in,out] ctx SHA-1 context
+ * @param[in] input buffer holding the data
+ * @param[in] ilen length of the input data
+ */
+void utils_sha1_update(IotSha1Context *ctx, const unsigned char *input, size_t ilen)
+{
+ size_t fill;
+ uint32_t left;
+
+ if (ilen == 0) {
+ return;
+ }
+
+ left = ctx->total[0] & 0x3F;
+ fill = 64 - left;
+
+ ctx->total[0] += (uint32_t)ilen;
+ ctx->total[0] &= 0xFFFFFFFF;
+
+ if (ctx->total[0] < (uint32_t)ilen) {
+ ctx->total[1]++;
+ }
+
+ if (left && ilen >= fill) {
+ memcpy((void *)(ctx->buffer + left), input, fill);
+ utils_sha1_process(ctx, ctx->buffer);
+ input += fill;
+ ilen -= fill;
+ left = 0;
+ }
+
+ while (ilen >= 64) {
+ utils_sha1_process(ctx, input);
+ input += 64;
+ ilen -= 64;
+ }
+
+ if (ilen > 0) {
+ memcpy((void *)(ctx->buffer + left), input, ilen);
+ }
+}
+
+static const unsigned char iot_sha1_padding[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+/**
+ * @brief SHA-1 final digest
+ *
+ * @param[in,out] ctx SHA-1 context
+ * @param[out] output SHA-1 checksum result
+ */
+void utils_sha1_finish(IotSha1Context *ctx, unsigned char output[20])
+{
+ uint32_t last, padn;
+ uint32_t high, low;
+ unsigned char msglen[8];
+
+ high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
+ low = (ctx->total[0] << 3);
+
+ IOT_SHA1_PUT_UINT32_BE(high, msglen, 0);
+ IOT_SHA1_PUT_UINT32_BE(low, msglen, 4);
+
+ last = ctx->total[0] & 0x3F;
+ padn = (last < 56) ? (56 - last) : (120 - last);
+
+ utils_sha1_update(ctx, iot_sha1_padding, padn);
+ utils_sha1_update(ctx, msglen, 8);
+
+ IOT_SHA1_PUT_UINT32_BE(ctx->state[0], output, 0);
+ IOT_SHA1_PUT_UINT32_BE(ctx->state[1], output, 4);
+ IOT_SHA1_PUT_UINT32_BE(ctx->state[2], output, 8);
+ IOT_SHA1_PUT_UINT32_BE(ctx->state[3], output, 12);
+ IOT_SHA1_PUT_UINT32_BE(ctx->state[4], output, 16);
+}
+
+/**
+ * @brief Output = SHA-1( input buffer )
+ *
+ * @param[in] input buffer holding the data
+ * @param[in] ilen length of the input data
+ * @param[out] output SHA-1 checksum result
+ */
+void utils_sha1(const unsigned char *input, size_t ilen, unsigned char output[20])
+{
+ IotSha1Context ctx;
+
+ utils_sha1_init(&ctx);
+ utils_sha1_starts(&ctx);
+ utils_sha1_update(&ctx, input, ilen);
+ utils_sha1_finish(&ctx, output);
+ utils_sha1_free(&ctx);
+}
+
+/**
+ * @brief Output = SHA-1( input buffer )
+ *
+ * @param[in] input buffer holding the data
+ * @param[in] ilen length of the input data
+ * @param[out] output SHA-1 checksum result hex
+ */
+void utils_sha1_hex(const unsigned char *input, size_t ilen, unsigned char output_hex[40])
+{
+ unsigned char out[20 + 1] = {0};
+ utils_sha1(input, ilen, out);
+ for (int i = 0; i < 20; ++i) {
+ output_hex[i * 2] = _hb2hex(out[i] >> 4);
+ output_hex[i * 2 + 1] = _hb2hex(out[i]);
+ }
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/cryptology/test/.clang-format b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/test/.clang-format
new file mode 100644
index 00000000..334c1c0d
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/test/.clang-format
@@ -0,0 +1,8 @@
+Language: Cpp
+BasedOnStyle: Google
+ColumnLimit: 120
+DerivePointerAlignment: true
+PointerAlignment: Left
+SortIncludes: true
+IncludeBlocks: Preserve
+IndentPPDirectives: AfterHash
\ No newline at end of file
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/cryptology/test/test_cryptology.cc b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/test/test_cryptology.cc
new file mode 100644
index 00000000..7fe3c4ab
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/cryptology/test/test_cryptology.cc
@@ -0,0 +1,179 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file test_cryptology.cc
+ * @brief test cryptology
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-07-07
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-07-07 1.0 fancyxu first commit
+ * 2021-07-08 1.1 fancyxu fix code standard of IotSha1Context
+ *
+ */
+
+#include
+#include
+
+#include "gtest/gtest.h"
+#include "utils_base64.h"
+#include "utils_hmac.h"
+#include "utils_md5.h"
+#include "utils_sha1.h"
+
+namespace cryptology_unittest {
+
+/**
+ * @brief Test base64.
+ *
+ */
+TEST(CryptologyTest, base64) {
+ const uint8_t base64_test_dec[64] = {0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD, 0xBF, 0x17, 0xD9, 0xA2, 0xC4,
+ 0x17, 0x1A, 0x01, 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09, 0x0C, 0xB6,
+ 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13, 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA,
+ 0x31, 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38, 0x00, 0x43, 0xE9, 0x54,
+ 0x97, 0xAF, 0x50, 0x4B, 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97};
+
+ const uint8_t base64_test_enc[] =
+ "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK"
+ "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw==";
+
+ size_t len;
+ uint8_t buffer[128];
+
+ ASSERT_EQ(utils_base64encode(buffer, sizeof(buffer), &len, base64_test_dec, sizeof(base64_test_dec)), 0);
+ ASSERT_EQ(memcmp(base64_test_enc, buffer, len), 0);
+ ASSERT_EQ(utils_base64decode(buffer, sizeof(buffer), &len, base64_test_enc, sizeof(base64_test_enc) - 1), 0);
+ ASSERT_EQ(memcmp(base64_test_dec, buffer, len), 0);
+}
+
+/**
+ * @brief Test hmac.
+ *
+ */
+TEST(CryptologyTest, hmac) {
+ /**
+ * @brief HMAC-SHA1
+ *
+ */
+ const char test_buf[] = "Here is a test for hmac-sha1!";
+ const uint8_t key[] = "0123456789123456";
+ const char result[] = "614b650ffefff7862c1bc7fdd9de4e472a8184c4";
+
+ char buf[128] = {0};
+
+ ASSERT_EQ(utils_hmac_sha1_hex(test_buf, strlen(test_buf), key, sizeof(key), buf), 0);
+ ASSERT_EQ(memcmp(buf, result, sizeof(result)), 0);
+}
+
+/**
+ * @brief Test sha1.
+ *
+ */
+TEST(CryptologyTest, sha1) {
+ /*
+ * FIPS-180-1 test vectors
+ */
+ const uint8_t sha1_test_buf[3][57] = {{"abc"}, {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, {""}};
+
+ const int sha1_test_buflen[3] = {3, 56, 1000};
+
+ const uint8_t sha1_test_sum[3][20] = {
+ {
+ 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E,
+ 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D,
+ },
+ {
+ 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE,
+ 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1,
+ },
+ {
+ 0x34, 0xAA, 0x97, 0x3C, 0xD4, 0xC4, 0xDA, 0xA4, 0xF6, 0x1E,
+ 0xEB, 0x2B, 0xDB, 0xAD, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6F,
+ },
+ };
+
+ int i, j, buflen, rc = 0;
+ uint8_t buf[1024];
+ uint8_t sha1sum[20];
+
+ IotSha1Context ctx;
+
+ utils_sha1_init(&ctx);
+
+ for (i = 0; i < 3; i++) {
+ utils_sha1_starts(&ctx);
+
+ if (i == 2) {
+ memset(buf, 'a', buflen = 1000);
+ for (j = 0; j < 1000; j++) {
+ utils_sha1_update(&ctx, buf, buflen);
+ }
+ } else {
+ utils_sha1_update(&ctx, sha1_test_buf[i], sha1_test_buflen[i]);
+ }
+
+ utils_sha1_finish(&ctx, sha1sum);
+ ASSERT_EQ(memcmp(sha1sum, sha1_test_sum[i], 20), 0);
+ }
+
+ utils_sha1_free(&ctx);
+
+ utils_sha1(sha1_test_buf[0], 3, sha1sum);
+ ASSERT_EQ(memcmp(sha1sum, sha1_test_sum[0], 20), 0);
+}
+
+/**
+ * @brief Test md5.
+ *
+ */
+TEST(CryptologyTest, md5) {
+ /*
+ * RFC 1321 test vectors
+ */
+ const uint8_t md5_test_buf[7][81] = {
+ {""},
+ {"a"},
+ {"abc"},
+ {"message digest"},
+ {"abcdefghijklmnopqrstuvwxyz"},
+ {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
+ {"12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
+ };
+
+ const size_t md5_test_buflen[7] = {0, 1, 3, 14, 26, 62, 80};
+
+ const char md5_test_sum[7][33] = {
+ {"D41D8CD98F00B204E9800998ECF8427E"}, {"0CC175B9C0F1B6A831C399E269772661"}, {"900150983CD24FB0D6963F7D28E17F72"},
+ {"F96B697D7CB7938D525A2F31AAF161D0"}, {"C3FCD3D76192E4007DFB496CCA67E13B"}, {"D174AB98D277D9F5A5611C2C9F419D9F"},
+ {"57EDF4A22BE3C955AC49DA2E2107B67A"},
+ };
+
+ /*
+ * Checkup routine
+ */
+ IotMd5Context md5_ctx;
+ for (int i = 0; i < 7; i++) {
+ utils_md5_reset(&md5_ctx);
+ utils_md5_update(&md5_ctx, md5_test_buf[i], md5_test_buflen[i]);
+ utils_md5_finish(&md5_ctx);
+ ASSERT_EQ(utils_md5_compare(&md5_ctx, md5_test_sum[i]), 0);
+ }
+}
+
+} // namespace cryptology_unittest
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/mqtt_packet/CMakeLists.txt b/components/connectivity/iot-hub-device-c-sdk/common/mqtt_packet/CMakeLists.txt
new file mode 100644
index 00000000..c46641a6
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/mqtt_packet/CMakeLists.txt
@@ -0,0 +1,10 @@
+file(GLOB src ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
+set(inc ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
+
+set(src_common ${src_common} ${src} PARENT_SCOPE)
+set(inc_common ${inc_common} ${inc} PARENT_SCOPE)
+
+if( ${CONFIG_IOT_TEST} STREQUAL "ON")
+ file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
+ set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
+endif()
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/mqtt_packet/inc/mqtt_packet.h b/components/connectivity/iot-hub-device-c-sdk/common/mqtt_packet/inc/mqtt_packet.h
new file mode 100644
index 00000000..8bdba289
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/mqtt_packet/inc/mqtt_packet.h
@@ -0,0 +1,335 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file mqtt_packet.h
+ * @brief header file for mqtt packet
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-24
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-24 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_MQTT_PACKET_INC_MQTT_PACKET_H_
+#define IOT_HUB_DEVICE_C_SDK_COMMON_MQTT_PACKET_INC_MQTT_PACKET_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+
+/**
+ * @brief header 1 byte + remaining length 1~4 byte(s).
+ *
+ */
+#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
+#define MAX_MQTT_FIXED_HEADER_LEN (1 + MAX_NO_OF_REMAINING_LENGTH_BYTES)
+#define MIN_MQTT_FIXED_HEADER_LEN (1 + 1)
+
+/**
+ * @brief Check if short buffer.
+ *
+ */
+#define SHORT_BUFFER_CHECK(buf_len, len) \
+ if (buf_len < len) { \
+ return MQTT_ERR_SHORT_BUFFER; \
+ }
+
+/**
+ * @brief Error code for mqtt packet。
+ *
+ */
+typedef enum {
+ MQTT_RET_PACKET_OK = 0,
+ MQTT_ERR_INVALID_PACKET_TYPE = -1,
+ MQTT_ERR_SHORT_BUFFER = -2,
+ MQTT_ERR_VERSION_UNSUPPORTED = -3,
+ MQTT_ERR_SUB_COUNT_EXCEED = -4,
+} MQTTPacketErrCode;
+
+/**
+ * @brief MQTT packet type.
+ *
+ */
+typedef enum {
+ CONNECT = 1,
+ CONNACK,
+ PUBLISH,
+ PUBACK,
+ PUBREC,
+ PUBREL,
+ PUBCOMP,
+ SUBSCRIBE,
+ SUBACK,
+ UNSUBSCRIBE,
+ UNSUBACK,
+ PINGREQ,
+ PINGRESP,
+ DISCONNECT
+} MQTTPacketType;
+
+/**
+ * @brief Defines the MQTT "Last Will and Testament" (LWT) settings for the connect packet.
+ *
+ */
+typedef struct {
+ const char* topic_name; /**< The LWT topic to which the LWT message will be published */
+ const char* message; /**< The LWT payload */
+ uint8_t retained; /**< The retained flag for the LWT message */
+ uint8_t qos; /**< The quality of service setting for the LWT message */
+} MQTTPacketWillOptions;
+
+/**
+ * @brief MQTT packet connect option.
+ *
+ */
+typedef struct {
+ uint8_t mqtt_version; /**< Version of MQTT to be used. 3 = 3.1 4 = 3.1.1 */
+ const char* client_id;
+ uint16_t keep_alive_interval;
+ uint8_t clean_session;
+ uint8_t will_flag;
+ MQTTPacketWillOptions will;
+ char* username;
+ char* password;
+} MQTTPacketConnectOption;
+
+/**
+ * @brief Bitfields for the MQTT header byte.
+ *
+ */
+typedef union {
+ uint8_t byte; /**< the whole byte */
+ struct {
+ uint8_t retain : 1; /**< retained flag bit */
+ uint8_t qos : 2; /**< QoS value, 0, 1 or 2 */
+ uint8_t dup : 1; /**< DUP flag bit */
+ uint8_t type : 4; /**< message type nibble */
+ } bits;
+} MQTTHeader;
+
+/**
+ * @brief Connect flags byte.
+ *
+ */
+typedef union {
+ uint8_t all; /**< all connect flags */
+
+ struct {
+ uint8_t : 1; /**< unused */
+ uint8_t clean_session : 1; /**< cleansession flag */
+ uint8_t will : 1; /**< will flag */
+ uint8_t will_qos : 2; /**< will QoS value */
+ uint8_t will_retain : 1; /**< will retain setting */
+ uint8_t password : 1; /**< 3.1 password */
+ uint8_t username : 1; /**< 3.1 user name */
+ } bits;
+} MQTTConnectFlags;
+
+/**
+ * @brief connack flags byte.
+ *
+ */
+typedef union {
+ unsigned char all; /**< all connack flags */
+ struct {
+ unsigned int sessionpresent : 1; /**< session present flag */
+ unsigned int reserved : 7; /**< unused */
+ } bits;
+} MQTTConnackFlags;
+
+/**
+ * @brief Flags for publish.
+ *
+ */
+typedef struct {
+ uint8_t dup;
+ uint8_t qos;
+ uint8_t retain;
+} MQTTPublishFlags;
+
+/**
+ * @brief Connect return code.
+ *
+ */
+typedef enum {
+ CONNACK_CONNECTION_ACCEPTED = 0, /**< connection accepted */
+ CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR = 1, /**< connection refused: unaccepted protocol version */
+ CONNACK_IDENTIFIER_REJECTED_ERROR = 2, /**< connection refused: identifier rejected */
+ CONNACK_SERVER_UNAVAILABLE_ERROR = 3, /**< connection refused: server unavailable */
+ CONNACK_BAD_USERDATA_ERROR = 4, /**< connection refused: bad user name or password */
+ CONNACK_NOT_AUTHORIZED_ERROR = 5 /**< connection refused: not authorized */
+} MQTTConnackReturnCodes;
+
+/**
+ * @brief Serialize the connect options into the buffer. See 3.1.
+ *
+ * @param[out] buf the buffer into which the packet will be serialized
+ * @param[in] buf_len the length in bytes of the supplied buffer
+ * @param[in] options the options to be used to build the connect packet
+ * @return serialized length, or error if <= 0
+ */
+int mqtt_connect_packet_serialize(uint8_t* buf, int buf_len, const MQTTPacketConnectOption* options);
+
+/**
+ * @brief Serialize a disconnect packet into the supplied buffer, ready for writing to a socket. See 3.14.
+ *
+ * @param[out] buf the buffer into which the packet will be serialized
+ * @param[in] buf_len the length in bytes of the supplied buffer, to avoid overruns
+ * @return serialized length, or error if <= 0
+ */
+int mqtt_disconnect_packet_serialize(uint8_t* buf, int buf_len);
+
+/**
+ * @brief Serialize a disconnect packet into the supplied buffer, ready for writing to a socket. See 3.12.
+ *
+ * @param[out] buf the buffer into which the packet will be serialized
+ * @param[in] buf_len the length in bytes of the supplied buffer, to avoid overruns
+ * @return serialized length, or error if <= 0
+ */
+int mqtt_pingreq_packet_serialize(uint8_t* buf, int buf_len);
+
+/**
+ * @brief Serialize the supplied publish data into the supplied buffer, ready for sending. See 3.3.
+ *
+ * @param[out] buf the buffer into which the packet will be serialized
+ * @param[in] buf_len the length in bytes of the supplied buffer
+ * @param[in] flags the MQTT dup, qos, retained flag
+ * @param[in] packet_id integer - the MQTT packet identifier
+ * @param[in] topic_name char * - the MQTT topic in the publish
+ * @param[in] payload byte buffer - the MQTT publish payload
+ * @param[in] payload_len integer - the length of the MQTT payload
+ * @return serialized length, or error if <= 0
+ */
+int mqtt_publish_packet_serialize(uint8_t* buf, int buf_len, const MQTTPublishFlags* flags, uint16_t packet_id,
+ const char* topic_name, const uint8_t* payload, int payload_len);
+
+/**
+ * @brief Serialize a puback packet into the supplied buffer. See 3.4.
+ *
+ * @param[out] buf the buffer into which the packet will be serialized
+ * @param[in] buf_len the length in bytes of the supplied buffer
+ * @param[in] packet_id integer - the MQTT packet identifier
+ * @return serialized length, or error if <= 0
+ */
+int mqtt_puback_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id);
+
+/**
+ * @brief Serialize the supplied subscribe data into the supplied buffer, ready for sending. See 3.8.
+ *
+ * @param[out] buf the buffer into which the packet will be serialized
+ * @param[in] buf_len the length in bytes of the supplied bufferr
+ * @param[in] packet_id integer - the MQTT packet identifier
+ * @param[in] count - number of members in the topicFilters and reqQos arrays
+ * @param[in] topic_filters - array of topic filter names
+ * @param[in] requested_qos - array of requested QoS
+ * @return serialized length, or error if <= 0
+ */
+int mqtt_subscribe_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id, uint32_t count,
+ char* topic_filters[], const int requested_qos[]);
+
+/**
+ * @brief Serialize the supplied unsubscribe data into the supplied buffer, ready for sending. See 3.10.
+ *
+ * @param[out] buf the raw buffer data, of the correct length determined by the remaining length field
+ * @param[in] buf_len the length in bytes of the data in the supplied buffer
+ * @param[in] packet_id integer - the MQTT packet identifier
+ * @param[in] count - number of members in the topicFilters array
+ * @param[in] topic_filters - array of topic filter names
+ * @return serialized length, or error if <= 0
+ */
+int mqtt_unsubscribe_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id, int count, char* topic_filters[]);
+
+/**
+ * @brief Deserialize the supplied (wire) buffer into connack data. See 3.2.
+ *
+ * @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
+ * @param[in] buf_len the length in bytes of the data in the supplied buffer
+ * @param[out] session_present the session present flag returned (only for MQTT 3.1.1)
+ * @param[out] connack_rc returned integer value of the connack return code
+ * @return @see MQTTPacketErrCode
+ */
+int mqtt_connack_packet_deserialize(uint8_t* buf, int buf_len, uint8_t* session_present, uint8_t* connack_rc);
+
+/**
+ * @brief Deserialize the supplied (wire) buffer into pingresp data. See 3.13.
+ *
+ * @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
+ * @param[in] buf_len the length in bytes of the data in the supplied buffer
+ * @return @see MQTTPacketErrCode
+ */
+int mqtt_pingresp_packet_deserialize(uint8_t* buf, int buf_len);
+
+/**
+ * @brief Deserialize the supplied (wire) buffer into publish data. See 3.3.
+ *
+ * @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
+ * @param[in] buf_len the length in bytes of the data in the supplied buffer
+ * @param[out] flags the MQTT dup, qos, retained flag
+ * @param[out] packet_id returned integer - the MQTT packet identifier
+ * @param[out] topic_name returned string - the MQTT topic in the publish
+ * @param[out] topic_len returned integer - the length of the MQTT topic
+ * @param[out] payload returned byte buffer - the MQTT publish payload
+ * @param[out] payload_len returned integer - the length of the MQTT payload
+ * @return @see MQTTPacketErrCode
+ */
+int mqtt_publish_packet_deserialize(uint8_t* buf, int buf_len, MQTTPublishFlags* flags, uint16_t* packet_id,
+ char** topic_name, int* topic_len, uint8_t** payload, int* payload_len);
+
+/**
+ * @brief Deserialize the supplied (wire) buffer into an ack. See 3.4.
+ *
+ * @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
+ * @param[in] buf_len the length in bytes of the data in the supplied buffer
+ * @param[out] packet_id returned integer - the MQTT packet identifier
+ * @return @see MQTTPacketErrCode
+ */
+int mqtt_puback_packet_deserialize(uint8_t* buf, int buf_len, uint16_t* packet_id);
+
+/**
+ * @brief Deserialize the supplied (wire) buffer into suback data. See 3.9.
+ *
+ * @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
+ * @param[in] buf_len the length in bytes of the data in the supplied buffer
+ * @param[in] maxcount - the maximum number of members allowed in the grantedQoSs array
+ * @param[out] count returned integer - number of members in the grantedQoSs array
+ * @param[out] packet_id returned integer - the MQTT packet identifier
+ * @param[out] granted_qos returned array of integers - the granted qualities of service
+ * @return @see MQTTPacketErrCode
+ */
+int mqtt_suback_packet_deserialize(uint8_t* buf, int buf_len, int maxcount, int* count, uint16_t* packet_id,
+ int granted_qos[]);
+
+/**
+ * @brief Deserialize the supplied (wire) buffer into unsuback data. See 3.11.
+ *
+ * @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
+ * @param[in] buf_len the length in bytes of the data in the supplied buffer
+ * @param[out] packet_id returned integer - the MQTT packet identifier
+ * @return @see MQTTPacketErrCode
+ */
+int mqtt_unsuback_packet_deserialize(uint8_t* buf, int buf_len, uint16_t* packet_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_COMMON_MQTT_PACKET_INC_MQTT_PACKET_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/mqtt_packet/src/mqtt_packet_deserialize.c b/components/connectivity/iot-hub-device-c-sdk/common/mqtt_packet/src/mqtt_packet_deserialize.c
new file mode 100644
index 00000000..27967d60
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/mqtt_packet/src/mqtt_packet_deserialize.c
@@ -0,0 +1,364 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file mqtt_packet_deserialize.c
+ * @brief implements mqtt packet deserialize. Reference paho.mqtt.embedded-c &
+ * http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.pdf
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-21
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-21 1.0 fancyxu first commit
+ *
+ */
+
+#include "mqtt_packet.h"
+
+/**
+ * @brief Reads one character from the input buffer. See 1.5.1.
+ *
+ * @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
+ * @return the character read
+ */
+static inline char _read_char(uint8_t** pptr)
+{
+ char c = **pptr;
+ (*pptr)++;
+ return c;
+}
+
+/**
+ * @brief Calculates an integer from two bytes read from the input buffer. See 1.5.2.
+ *
+ * @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
+ * @return the integer value calculated
+ */
+static inline uint16_t _read_int(uint8_t** pptr)
+{
+ uint8_t* ptr = *pptr;
+ int len = 256 * (*ptr) + (*(ptr + 1));
+ *pptr += 2;
+ return (uint16_t)len;
+}
+
+/**
+ * @brief Read a UTF-8 encoded string pointer from the input buffer. See 1.5.3.
+ *
+ * @param[out] pstring the pointer of the C string to read.
+ * @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
+ * @return the length of string
+ */
+static inline int _read_string(char** pstring, uint8_t** pptr)
+{
+ int str_len = _read_int(pptr);
+ if (str_len > 0) {
+ *pstring = (char*)*pptr;
+ *pptr += str_len;
+ }
+
+ return str_len;
+}
+
+/**
+ * @brief Check the mqtt type and get publish flags. See 2.2.
+ *
+ * @param[in] packet_type type of mqtt control packet, See 2.2.1
+ * @param[in] pflags pointer to the flags of mqtt control packet fixed header. See 2.2.2
+ * @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
+ * @return @see MQTTPacketErrCode
+ */
+static int _mqtt_packet_type_check(MQTTPacketType packet_type, MQTTPublishFlags* pflags, uint8_t** pptr)
+{
+ MQTTHeader header = {0};
+
+ header.byte = _read_char(pptr);
+
+ switch (header.bits.type) {
+ case PUBLISH:
+ pflags->dup = header.bits.dup;
+ pflags->qos = header.bits.qos;
+ pflags->retain = header.bits.retain;
+ break;
+ case SUBSCRIBE:
+ case UNSUBSCRIBE:
+ if ((header.byte & 0xf0) != 0x10) {
+ return MQTT_ERR_INVALID_PACKET_TYPE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return packet_type != header.bits.type ? MQTT_ERR_INVALID_PACKET_TYPE : MQTT_RET_PACKET_OK;
+}
+
+/**
+ * @brief Deserialize remaining length. See 2.2.3.
+ *
+ * @param[in] buf_len the length in bytes of the supplied buffer
+ * @param[in] plen the pointer to the remaining length
+ * @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
+ * @return @see MQTTPacketErrCode
+ */
+static int _mqtt_remaining_length_deserialize(int buf_len, int* plen, uint8_t** pptr)
+{
+ uint8_t* buf = *pptr;
+ uint8_t c = 0;
+ uint32_t multiplier = 1;
+ uint32_t count = 0;
+ uint32_t len = 0;
+
+ do {
+ if (++count > MAX_NO_OF_REMAINING_LENGTH_BYTES) {
+ return MQTT_ERR_INVALID_PACKET_TYPE;
+ }
+ c = *buf++;
+ len += (c & 127) * multiplier;
+ multiplier *= 128;
+
+ SHORT_BUFFER_CHECK(buf_len, count + 1 + len);
+ } while (c & 128);
+
+ if (plen) {
+ *plen = len;
+ }
+
+ *pptr += count;
+ return MQTT_RET_PACKET_OK;
+}
+
+/**
+ * @brief Deserialize the supplied (wire) buffer into connack data. See 3.2.
+ *
+ * @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
+ * @param[in] buf_len the length in bytes of the data in the supplied buffer
+ * @param[out] session_present the session present flag returned (only for MQTT 3.1.1)
+ * @param[out] connack_rc returned integer value of the connack return code
+ * @return @see MQTTPacketErrCode
+ */
+int mqtt_connack_packet_deserialize(uint8_t* buf, int buf_len, uint8_t* session_present, uint8_t* connack_rc)
+{
+ SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
+
+ uint8_t* ptr = buf;
+ int rc = 0;
+ MQTTConnackFlags flags = {0};
+
+ rc = _mqtt_packet_type_check(CONNACK, NULL, &ptr);
+ if (rc) {
+ goto exit;
+ }
+
+ rc = _mqtt_remaining_length_deserialize(buf_len, NULL, &ptr);
+ if (rc < 0) {
+ goto exit;
+ }
+
+ flags.all = _read_char(&ptr);
+ *session_present = flags.bits.sessionpresent;
+ *connack_rc = _read_char(&ptr);
+exit:
+ return rc;
+}
+
+/**
+ * @brief Deserialize the supplied (wire) buffer into pingresp data. See 3.13.
+ *
+ * @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
+ * @param[in] buf_len the length in bytes of the data in the supplied buffer
+ * @return @see MQTTPacketErrCode
+ */
+int mqtt_pingresp_packet_deserialize(uint8_t* buf, int buf_len)
+{
+ SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
+
+ uint8_t* ptr = buf;
+ int rc = 0;
+
+ rc = _mqtt_packet_type_check(PINGRESP, NULL, &ptr);
+ if (rc) {
+ goto exit;
+ }
+
+ rc = _mqtt_remaining_length_deserialize(buf_len, NULL, &ptr);
+ if (rc < 0) {
+ goto exit;
+ }
+exit:
+ return rc;
+}
+
+/**
+ * @brief Deserialize the supplied (wire) buffer into publish data. See 3.3.
+ *
+ * @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
+ * @param[in] buf_len the length in bytes of the data in the supplied buffer
+ * @param[out] flags the MQTT dup, qos, retained flag
+ * @param[out] packet_id returned integer - the MQTT packet identifier
+ * @param[out] topic_name returned string - the MQTT topic in the publish
+ * @param[out] topic_len returned integer - the length of the MQTT topic
+ * @param[out] payload returned byte buffer - the MQTT publish payload
+ * @param[out] payload_len returned integer - the length of the MQTT payload
+ * @return @see MQTTPacketErrCode
+ */
+int mqtt_publish_packet_deserialize(uint8_t* buf, int buf_len, MQTTPublishFlags* flags, uint16_t* packet_id,
+ char** topic_name, int* topic_len, uint8_t** payload, int* payload_len)
+{
+ SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
+
+ uint8_t* ptr = buf;
+ uint8_t* ptr_remain = NULL;
+ int rc = 0;
+ int len = 0;
+
+ rc = _mqtt_packet_type_check(PUBLISH, flags, &ptr);
+ if (rc) {
+ goto exit;
+ }
+
+ rc = _mqtt_remaining_length_deserialize(buf_len, &len, &ptr);
+ if (rc) {
+ goto exit;
+ }
+
+ ptr_remain = ptr;
+
+ rc = _read_string(topic_name, &ptr);
+ if (rc <= 0) {
+ rc = MQTT_ERR_INVALID_PACKET_TYPE;
+ goto exit;
+ }
+ *topic_len = rc;
+
+ if (flags->qos > 0) {
+ *packet_id = _read_int(&ptr);
+ }
+
+ *payload_len = len - (ptr - ptr_remain);
+ *payload = ptr;
+
+ rc = MQTT_RET_PACKET_OK;
+exit:
+ return rc;
+}
+
+/**
+ * @brief Deserialize the supplied (wire) buffer into an ack. See 3.4.
+ *
+ * @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
+ * @param[in] buf_len the length in bytes of the data in the supplied buffer
+ * @param[out] packet_id returned integer - the MQTT packet identifier
+ * @return @see MQTTPacketErrCode
+ */
+int mqtt_puback_packet_deserialize(uint8_t* buf, int buf_len, uint16_t* packet_id)
+{
+ SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
+
+ uint8_t* ptr = buf;
+ int rc = 0;
+
+ rc = _mqtt_packet_type_check(PUBACK, 0, &ptr);
+ if (rc) {
+ goto exit;
+ }
+
+ rc = _mqtt_remaining_length_deserialize(buf_len, NULL, &ptr);
+ if (rc) {
+ goto exit;
+ }
+
+ *packet_id = _read_int(&ptr);
+exit:
+ return rc;
+}
+
+/**
+ * @brief Deserialize the supplied (wire) buffer into suback data. See 3.9.
+ *
+ * @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
+ * @param[in] buf_len the length in bytes of the data in the supplied buffer
+ * @param[in] maxcount - the maximum number of members allowed in the grantedQoSs array
+ * @param[out] count returned integer - number of members in the grantedQoSs array
+ * @param[out] packet_id returned integer - the MQTT packet identifier
+ * @param[out] granted_qos returned array of integers - the granted qualities of service
+ * @return @see MQTTPacketErrCode
+ */
+int mqtt_suback_packet_deserialize(uint8_t* buf, int buf_len, int maxcount, int* count, uint16_t* packet_id,
+ int granted_qos[])
+{
+ SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
+
+ uint8_t* ptr = buf;
+ int rc = 0;
+ int len = 0;
+
+ rc = _mqtt_packet_type_check(SUBACK, NULL, &ptr);
+ if (rc) {
+ goto exit;
+ }
+
+ rc = _mqtt_remaining_length_deserialize(buf_len, &len, &ptr);
+ if (rc) {
+ goto exit;
+ }
+
+ *packet_id = _read_int(&ptr);
+ len -= 2;
+
+ *count = 0;
+ while (len--) {
+ if (*count > maxcount) {
+ rc = MQTT_ERR_SUB_COUNT_EXCEED;
+ goto exit;
+ }
+ granted_qos[(*count)++] = _read_char(&ptr);
+ }
+
+exit:
+ return rc;
+}
+
+/**
+ * @brief Deserialize the supplied (wire) buffer into unsuback data. See 3.11.
+ *
+ * @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
+ * @param[in] buf_len the length in bytes of the data in the supplied buffer
+ * @param[out] packet_id returned integer - the MQTT packet identifier
+ * @return @see MQTTPacketErrCode
+ */
+int mqtt_unsuback_packet_deserialize(uint8_t* buf, int buf_len, uint16_t* packet_id)
+{
+ SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
+
+ uint8_t* ptr = buf;
+ int rc = 0;
+
+ rc = _mqtt_packet_type_check(UNSUBACK, NULL, &ptr);
+ if (rc) {
+ goto exit;
+ }
+
+ rc = _mqtt_remaining_length_deserialize(buf_len, NULL, &ptr);
+ if (rc) {
+ goto exit;
+ }
+
+ *packet_id = _read_int(&ptr);
+exit:
+ return rc;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/mqtt_packet/src/mqtt_packet_serialize.c b/components/connectivity/iot-hub-device-c-sdk/common/mqtt_packet/src/mqtt_packet_serialize.c
new file mode 100644
index 00000000..e1b7f482
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/mqtt_packet/src/mqtt_packet_serialize.c
@@ -0,0 +1,458 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file mqtt_packet_serialize.c
+ * @brief implements mqtt packet serialize. Reference paho.mqtt.embedded-c &
+ * http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.pdf
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-21
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-21 1.0 fancyxu first commit
+ *
+ */
+
+#include "mqtt_packet.h"
+
+/**
+ * @brief Writes one character to an output buffer. See 1.5.1.
+ *
+ * @param[in] c the character to write
+ * @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ */
+static inline void _write_char(char c, uint8_t** pptr)
+{
+ **pptr = c;
+ (*pptr)++;
+}
+
+/**
+ * @brief Writes an integer as 2 bytes to an output buffer. See 1.5.2.
+ *
+ * @param[in] value the integer to write.
+ * @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned.
+ */
+static inline void _write_int(int value, uint8_t** pptr)
+{
+ **pptr = (uint8_t)(value / 256);
+ (*pptr)++;
+ **pptr = (uint8_t)(value % 256);
+ (*pptr)++;
+}
+
+/**
+ * @brief Writes a UTF-8 encoded string to an output buffer. Converts C string to
+ * length-delimited. See 1.5.3.
+ *
+ * @param[in] string the C string to write
+ * @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ */
+static inline void _write_string(const char* string, uint8_t** pptr)
+{
+ int len = strlen(string);
+ _write_int(len, pptr);
+ memcpy(*pptr, string, len);
+ *pptr += len;
+}
+
+/**
+ * @brief Serialize mqtt fixed header except remaining length. See 2.2.
+ *
+ * @param[in] packet_type type of mqtt control packet, See 2.2.1 MQTT Control Packet type
+ * @param[in] pflags pointer to the flags of mqtt control packet fixed header. See 2.2.2 Flags
+ * @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ */
+static void _mqtt_header_serialize(MQTTPacketType packet_type, const MQTTPublishFlags* pflags, uint8_t** pptr)
+{
+ MQTTHeader header = {0};
+
+ switch (packet_type) {
+ case PUBLISH:
+ header.bits.dup = pflags->dup;
+ header.bits.qos = pflags->qos;
+ header.bits.retain = pflags->retain;
+ break;
+ case SUBSCRIBE:
+ case UNSUBSCRIBE:
+ header.byte = 0x02;
+ break;
+ default:
+ break;
+ }
+
+ header.bits.type |= packet_type;
+ _write_char(header.byte, pptr); /* write header */
+}
+
+/**
+ * @brief Serialize remaining length. See 2.2.3.
+ *
+ * @param[in] length the length to be encoded to remaining length
+ * @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ */
+static void _mqtt_remaining_length_serialize(int length, uint8_t** pptr)
+{
+ char d;
+ do {
+ d = length % 128;
+ length /= 128;
+
+ // if there are more digits to encode, set the top bit of this digit
+ if (length > 0) {
+ d |= 0x80;
+ }
+
+ _write_char(d, pptr);
+ } while (length > 0);
+}
+
+/**
+ * @brief Serialize the remaining length of the MQTT connect packet that would be produced using the
+ * supplied connect options. See 3.1.1.
+ *
+ * @param[in] options the options to be used to build the connect packet
+ * @param[in] buf_len the length in bytes of the supplied buffer
+ * @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ */
+static int _mqtt_connect_remaining_length_serialize(const MQTTPacketConnectOption* options, int buf_len, uint8_t** pptr)
+{
+ if (options->mqtt_version != 3 && options->mqtt_version != 4) {
+ return MQTT_ERR_VERSION_UNSUPPORTED;
+ }
+
+ int len = 0;
+
+ len = options->mqtt_version == 3 ? 12 : 10;
+
+ len += strlen(options->client_id) + 2;
+
+ if (options->will_flag) {
+ len += strlen(options->will.topic_name) + 2 + strlen(options->will.message) + 2;
+ }
+
+ if (options->username) {
+ len += strlen(options->username) + 2;
+ }
+
+ if (options->password) {
+ len += strlen(options->password) + 2;
+ }
+
+ SHORT_BUFFER_CHECK(buf_len, len);
+
+ _mqtt_remaining_length_serialize(len, pptr);
+
+ return MQTT_RET_PACKET_OK;
+}
+
+/**
+ * @brief Serialize the mqtt connect packet variable header. See 3.1.2.
+ *
+ * @param[in] options the options to be used to build the connect packet
+ * @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ */
+static int _mqtt_connect_variable_header_serialize(const MQTTPacketConnectOption* options, uint8_t** pptr)
+{
+ if (options->password && !options->username) {
+ return MQTT_ERR_INVALID_PACKET_TYPE;
+ }
+
+ // 3.1.2.1 protocol name & 3.1.2.2 protocol level
+ switch (options->mqtt_version) {
+ case 3:
+ _write_string("MQIsdp", pptr);
+ _write_char((char)3, pptr);
+ break;
+ case 4:
+ _write_string("MQTT", pptr);
+ _write_char((char)4, pptr);
+ break;
+ }
+
+ // 3.1.2.3 Connect Flags
+ MQTTConnectFlags flags = {
+ .bits.clean_session = options->clean_session,
+ .bits.will = options->will_flag,
+ .bits.will_qos = options->will_flag ? options->will.qos : 0,
+ .bits.will_retain = options->will_flag ? options->will.retained : 0,
+ .bits.username = options->username ? 1 : 0,
+ .bits.password = options->password ? 1 : 0,
+ };
+ _write_char(flags.all, pptr);
+
+ return MQTT_RET_PACKET_OK;
+}
+
+/**
+ * @brief Serialize the remaining length of the MQTT publish packet that would be produced using the supplied
+ * parameters. See 3.3.2 & 3.3.3.
+ *
+ * @param[in] qos the MQTT QoS of the publish (packet id is omitted for QoS 0)
+ * @param[in] topic_name the topic name to be used in the publish
+ * @param[in] payload_len the length of the payload to be sent
+ * @param[in] buf_len the length in bytes of the supplied buffer
+ * @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ */
+static int _mqtt_publish_remaining_length_serialize(uint8_t qos, const char* topic_name, int payload_len, int buf_len,
+ uint8_t** pptr)
+{
+ int len = 0;
+
+ len += 2 + strlen(topic_name) + payload_len;
+ if (qos > 0) {
+ len += 2; /* packet id */
+ }
+
+ SHORT_BUFFER_CHECK(buf_len, len + MAX_MQTT_FIXED_HEADER_LEN);
+
+ _mqtt_remaining_length_serialize(len, pptr);
+ return MQTT_RET_PACKET_OK;
+}
+
+/**
+ * @brief Serialize the length of the MQTT subscribe/unsubscribe packet that would be produced using the
+ * supplied parameters. See 3.8.1 & 3.10.1.
+ *
+ * @param[in] count the number of topic filter strings in topicFilters
+ * @param[in] topic_filters the array of topic filter strings to be used in the publish
+ * @param[in] buf_len the length in bytes of the supplied buffer
+ * @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ */
+static int _mqtt_sub_unsub_remaining_length_serialize(uint32_t count, char* topic_filters[], int buf_len,
+ uint8_t** pptr, uint8_t is_sub)
+{
+ int i = 0;
+ int len = 2; /* packet id */
+
+ for (i = 0; i < count; ++i) {
+ len += 2 + strlen(topic_filters[i]) + is_sub; /* length + topic */
+ }
+
+ SHORT_BUFFER_CHECK(buf_len, len + MAX_MQTT_FIXED_HEADER_LEN);
+
+ _mqtt_remaining_length_serialize(len, pptr);
+ return MQTT_RET_PACKET_OK;
+}
+
+/**
+ * @brief Serialize the connect options into the buffer. See 3.1.
+ *
+ * @param[out] buf the buffer into which the packet will be serialized
+ * @param[in] buf_len the length in bytes of the supplied buffer
+ * @param[in] options the options to be used to build the connect packet
+ * @return serialized length, or error if <= 0
+ */
+int mqtt_connect_packet_serialize(uint8_t* buf, int buf_len, const MQTTPacketConnectOption* options)
+{
+ SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
+ int rc;
+ uint8_t* ptr = buf;
+
+ // 3.1.1 Fixed header
+ _mqtt_header_serialize(CONNECT, NULL, &ptr);
+
+ rc = _mqtt_connect_remaining_length_serialize(options, buf_len, &ptr);
+ if (rc) {
+ return rc;
+ }
+
+ // 3.1.2 Variable header
+ rc = _mqtt_connect_variable_header_serialize(options, &ptr);
+ if (rc) {
+ return rc;
+ }
+
+ // 3.1.3 Payload
+ _write_int(options->keep_alive_interval, &ptr);
+ _write_string(options->client_id, &ptr);
+
+ if (options->will_flag) {
+ _write_string(options->will.topic_name, &ptr);
+ _write_string(options->will.message, &ptr);
+ }
+
+ if (options->username) {
+ _write_string(options->username, &ptr);
+ }
+
+ if (options->password) {
+ _write_string(options->password, &ptr);
+ }
+
+ return ptr - buf;
+}
+
+/**
+ * @brief Serialize a disconnect packet into the supplied buffer, ready for writing to a socket. See 3.14.
+ *
+ * @param[out] buf the buffer into which the packet will be serialized
+ * @param[in] buf_len the length in bytes of the supplied buffer, to avoid overruns
+ * @return serialized length, or error if <= 0
+ */
+int mqtt_disconnect_packet_serialize(uint8_t* buf, int buf_len)
+{
+ SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
+
+ uint8_t* ptr = buf;
+ _mqtt_header_serialize(DISCONNECT, 0, &ptr);
+ _mqtt_remaining_length_serialize(0, &ptr);
+ return ptr - buf;
+}
+
+/**
+ * @brief Serialize a disconnect packet into the supplied buffer, ready for writing to a socket. See 3.12.
+ *
+ * @param[out] buf the buffer into which the packet will be serialized
+ * @param[in] buf_len the length in bytes of the supplied buffer, to avoid overruns
+ * @return serialized length, or error if <= 0
+ */
+int mqtt_pingreq_packet_serialize(uint8_t* buf, int buf_len)
+{
+ SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
+
+ uint8_t* ptr = buf;
+ _mqtt_header_serialize(PINGREQ, 0, &ptr);
+ _mqtt_remaining_length_serialize(0, &ptr);
+ return ptr - buf;
+}
+
+/**
+ * @brief Serialize the supplied publish data into the supplied buffer, ready for sending. See 3.3.
+ *
+ * @param[out] buf the buffer into which the packet will be serialized
+ * @param[in] buf_len the length in bytes of the supplied buffer
+ * @param[in] flags the MQTT dup, qos, retained flag
+ * @param[in] packet_id integer - the MQTT packet identifier
+ * @param[in] topic_name char * - the MQTT topic in the publish
+ * @param[in] payload byte buffer - the MQTT publish payload
+ * @param[in] payload_len integer - the length of the MQTT payload
+ * @return serialized length, or error if <= 0
+ */
+int mqtt_publish_packet_serialize(uint8_t* buf, int buf_len, const MQTTPublishFlags* flags, uint16_t packet_id,
+ const char* topic_name, const uint8_t* payload, int payload_len)
+{
+ SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
+
+ int rc;
+ uint8_t* ptr = buf;
+
+ _mqtt_header_serialize(PUBLISH, flags, &ptr);
+
+ rc = _mqtt_publish_remaining_length_serialize(flags->qos, topic_name, payload_len, buf_len, &ptr);
+ if (rc) {
+ return rc;
+ }
+
+ _write_string(topic_name, &ptr);
+
+ if (flags->qos > 0) {
+ _write_int(packet_id, &ptr);
+ }
+
+ memcpy(ptr, payload, payload_len);
+
+ ptr += payload_len;
+ return ptr - buf;
+}
+
+/**
+ * @brief Serialize a puback packet into the supplied buffer. See 3.4.
+ *
+ * @param[out] buf the buffer into which the packet will be serialized
+ * @param[in] buf_len the length in bytes of the supplied buffer
+ * @param[in] packet_id integer - the MQTT packet identifier
+ * @return serialized length, or error if <= 0
+ */
+int mqtt_puback_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id)
+{
+ SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN + 2);
+
+ uint8_t* ptr = buf;
+
+ _mqtt_header_serialize(PUBACK, 0, &ptr);
+ _mqtt_remaining_length_serialize(2, &ptr);
+ _write_int(packet_id, &ptr);
+ return ptr - buf;
+}
+
+/**
+ * @brief Serialize the supplied subscribe data into the supplied buffer, ready for sending. See 3.8.
+ *
+ * @param[out] buf the buffer into which the packet will be serialized
+ * @param[in] buf_len the length in bytes of the supplied bufferr
+ * @param[in] packet_id integer - the MQTT packet identifier
+ * @param[in] count - number of members in the topicFilters and reqQos arrays
+ * @param[in] topic_filters - array of topic filter names
+ * @param[in] requested_qos - array of requested QoS
+ * @return serialized length, or error if <= 0
+ */
+int mqtt_subscribe_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id, uint32_t count,
+ char* topic_filters[], const int requested_qos[])
+{
+ SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
+
+ int rc, i = 0;
+ uint8_t* ptr = buf;
+
+ _mqtt_header_serialize(SUBSCRIBE, 0, &ptr);
+ rc = _mqtt_sub_unsub_remaining_length_serialize(count, topic_filters, buf_len, &ptr, 1);
+ if (rc) {
+ return rc;
+ }
+
+ _write_int(packet_id, &ptr);
+
+ for (i = 0; i < count; ++i) {
+ _write_string(topic_filters[i], &ptr);
+ _write_char(requested_qos[i], &ptr);
+ }
+
+ return ptr - buf;
+}
+
+/**
+ * @brief Serialize the supplied unsubscribe data into the supplied buffer, ready for sending. See 3.10.
+ *
+ * @param[out] buf the raw buffer data, of the correct length determined by the remaining length field
+ * @param[in] buf_len the length in bytes of the data in the supplied buffer
+ * @param[in] packet_id integer - the MQTT packet identifier
+ * @param[in] count - number of members in the topicFilters array
+ * @param[in] topic_filters - array of topic filter names
+ * @return serialized length, or error if <= 0
+ */
+int mqtt_unsubscribe_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id, int count, char* topic_filters[])
+{
+ SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
+
+ int rc, i = 0;
+ uint8_t* ptr = buf;
+
+ _mqtt_header_serialize(UNSUBSCRIBE, 0, &ptr);
+ rc = _mqtt_sub_unsub_remaining_length_serialize(count, topic_filters, buf_len, &ptr, 0);
+ if (rc) {
+ return rc;
+ }
+
+ _write_int(packet_id, &ptr);
+
+ for (i = 0; i < count; ++i) {
+ _write_string(topic_filters[i], &ptr);
+ }
+
+ return ptr - buf;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/mqtt_packet/test/.clang-format b/components/connectivity/iot-hub-device-c-sdk/common/mqtt_packet/test/.clang-format
new file mode 100644
index 00000000..334c1c0d
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/mqtt_packet/test/.clang-format
@@ -0,0 +1,8 @@
+Language: Cpp
+BasedOnStyle: Google
+ColumnLimit: 120
+DerivePointerAlignment: true
+PointerAlignment: Left
+SortIncludes: true
+IncludeBlocks: Preserve
+IndentPPDirectives: AfterHash
\ No newline at end of file
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/mqtt_packet/test/test_mqtt_packet.cc b/components/connectivity/iot-hub-device-c-sdk/common/mqtt_packet/test/test_mqtt_packet.cc
new file mode 100644
index 00000000..959b4114
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/mqtt_packet/test/test_mqtt_packet.cc
@@ -0,0 +1,307 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file test_mqtt_packet.cc
+ * @brief unit test for mqtt packet
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-06-01
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-06-01 1.0 fancyxu first commit
+ *
+ */
+
+#include
+#include
+
+#include "gtest/gtest.h"
+#include "mqtt_packet.h"
+
+namespace mqtt_packet_unittest {
+/**
+ * @brief Test connect packet.
+ *
+ */
+TEST(MQTTPacketTest, CONNECT) {
+ uint8_t test_packet[] = {
+ 0x10, 0x77, 0x00, 0x04, 0x4d, 0x51, 0x54, 0x54, 0x04, 0xc2, 0x00, 0xf0, 0x00, 0x0e, 0x4a, 0x4e, 0x4c, 0x52,
+ 0x57, 0x4f, 0x33, 0x54, 0x35, 0x39, 0x74, 0x65, 0x73, 0x74, 0x00, 0x28, 0x4a, 0x4e, 0x4c, 0x52, 0x57, 0x4f,
+ 0x33, 0x54, 0x35, 0x39, 0x74, 0x65, 0x73, 0x74, 0x3b, 0x32, 0x31, 0x30, 0x31, 0x30, 0x34, 0x30, 0x36, 0x3b,
+ 0x49, 0x41, 0x5a, 0x5a, 0x4a, 0x3b, 0x32, 0x31, 0x34, 0x37, 0x34, 0x38, 0x33, 0x36, 0x34, 0x37, 0x00, 0x31,
+ 0x66, 0x62, 0x63, 0x39, 0x65, 0x34, 0x36, 0x33, 0x61, 0x32, 0x30, 0x64, 0x66, 0x30, 0x34, 0x35, 0x65, 0x62,
+ 0x66, 0x34, 0x38, 0x34, 0x35, 0x61, 0x35, 0x39, 0x62, 0x32, 0x61, 0x38, 0x35, 0x66, 0x65, 0x62, 0x34, 0x31,
+ 0x34, 0x31, 0x30, 0x30, 0x3b, 0x68, 0x6d, 0x61, 0x63, 0x73, 0x68, 0x61, 0x31,
+ };
+
+ uint8_t packet_buf[2048];
+ std::string user_name = "JNLRWO3T59test;21010406;IAZZJ;2147483647";
+ std::string password = "fbc9e463a20df045ebf4845a59b2a85feb414100;hmacsha1";
+
+ MQTTPacketConnectOption options = {
+ .mqtt_version = 4,
+ .client_id = "JNLRWO3T59test",
+ .keep_alive_interval = 240,
+ .clean_session = 1,
+ .will_flag = 0,
+ .will = {0},
+ .username = reinterpret_cast(const_cast(user_name.c_str())),
+ .password = reinterpret_cast(const_cast(password.c_str())),
+ };
+
+ ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, sizeof(packet_buf), &options), sizeof(test_packet));
+ ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
+
+ ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, 1, &options), MQTT_ERR_SHORT_BUFFER);
+
+ options.username = NULL;
+ ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, sizeof(packet_buf), &options), MQTT_ERR_INVALID_PACKET_TYPE);
+
+ options.password = NULL;
+ ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, sizeof(packet_buf), &options),
+ sizeof(test_packet) - user_name.length() - password.length() - 4);
+
+ options.mqtt_version = 5;
+ ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, sizeof(packet_buf), &options), MQTT_ERR_VERSION_UNSUPPORTED);
+}
+
+/**
+ * @brief Test connack packet.
+ *
+ */
+TEST(MQTTPacketTest, CONNACK) {
+ uint8_t test_packet[] = {0x20, 0x02, 0x00, 0x00};
+ uint8_t session_present, connack_rc;
+
+ for (uint8_t i = CONNACK_CONNECTION_ACCEPTED; i <= CONNACK_NOT_AUTHORIZED_ERROR; i++) {
+ test_packet[3] = i;
+ ASSERT_EQ(mqtt_connack_packet_deserialize(test_packet, sizeof(test_packet), &session_present, &connack_rc),
+ MQTT_RET_PACKET_OK);
+ ASSERT_EQ(connack_rc, i);
+ }
+
+ ASSERT_EQ(mqtt_connack_packet_deserialize(test_packet, 2, &session_present, &connack_rc), MQTT_ERR_SHORT_BUFFER);
+
+ test_packet[0] = SUBACK;
+ ASSERT_EQ(mqtt_connack_packet_deserialize(test_packet, sizeof(test_packet), &session_present, &connack_rc),
+ MQTT_ERR_INVALID_PACKET_TYPE);
+}
+
+/**
+ * @brief Test publish packet.
+ *
+ */
+TEST(MQTTPacketTest, PUBLISH) {
+ uint8_t test_packet[] = {
+ 0x32, 0x40, 0x00, 0x14, 0x4a, 0x4e, 0x4c, 0x52, 0x57, 0x4f, 0x33, 0x54, 0x35, 0x39, 0x2f, 0x74, 0x65,
+ 0x73, 0x74, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x70, 0xaf, 0x7b, 0x22, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+ 0x22, 0x3a, 0x20, 0x22, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x22,
+ 0x2c, 0x20, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x30, 0x22, 0x7d,
+ };
+
+ uint8_t packet_buf[128];
+
+ /**
+ * @brief QoS1
+ *
+ */
+ char serialize_topic_name[] = "JNLRWO3T59/test/data";
+ int serialize_topic_len = strlen(serialize_topic_name);
+ uint16_t serialize_packet_id = 28847;
+ uint8_t serialize_payload[] = "{\"action\": \"publish_test\", \"count\": \"0\"}";
+ int serialize_payload_len = strlen((const char *)serialize_payload);
+ MQTTPublishFlags serialize_flags = {0, 1, 0};
+
+ ASSERT_EQ(mqtt_publish_packet_serialize(packet_buf, sizeof(packet_buf), &serialize_flags, serialize_packet_id,
+ serialize_topic_name, serialize_payload, serialize_payload_len),
+ sizeof(test_packet));
+ ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
+
+ char *deserialize_topic_name;
+ int deserialize_topic_len;
+ uint16_t deserialize_packet_id = 0;
+ uint8_t *deserialize_payload;
+ int deserialize_payload_len;
+ MQTTPublishFlags deserialize_flags;
+ ASSERT_EQ(mqtt_publish_packet_deserialize(test_packet, sizeof(test_packet), &deserialize_flags,
+ &deserialize_packet_id, &deserialize_topic_name, &deserialize_topic_len,
+ &deserialize_payload, &deserialize_payload_len),
+ 0);
+
+ ASSERT_EQ(deserialize_packet_id, serialize_packet_id);
+ ASSERT_EQ(deserialize_topic_len, serialize_topic_len);
+ ASSERT_EQ(deserialize_payload_len, serialize_payload_len);
+
+ ASSERT_EQ(memcmp(&deserialize_flags, &serialize_flags, sizeof(deserialize_flags)), 0);
+ ASSERT_EQ(memcmp(deserialize_topic_name, serialize_topic_name, deserialize_topic_len), 0);
+ ASSERT_EQ(memcmp(deserialize_payload, serialize_payload, deserialize_payload_len), 0);
+
+ /**
+ * @brief QoS0
+ *
+ */
+ serialize_flags.qos = 0;
+ deserialize_packet_id = 0;
+ ASSERT_EQ(
+ mqtt_publish_packet_serialize(packet_buf, sizeof(packet_buf), &serialize_flags, serialize_packet_id,
+ serialize_topic_name, serialize_payload, strlen((const char *)serialize_payload)),
+ sizeof(test_packet) - 2);
+ ASSERT_EQ(mqtt_publish_packet_deserialize(packet_buf, sizeof(packet_buf), &deserialize_flags, &deserialize_packet_id,
+ &deserialize_topic_name, &deserialize_topic_len, &deserialize_payload,
+ &deserialize_payload_len),
+ MQTT_RET_PACKET_OK);
+ ASSERT_NE(deserialize_packet_id, serialize_packet_id);
+
+ /**
+ * @brief bad packet
+ *
+ */
+ packet_buf[0] = PUBACK;
+ ASSERT_EQ(mqtt_publish_packet_deserialize(packet_buf, sizeof(packet_buf), &deserialize_flags, &deserialize_packet_id,
+ &deserialize_topic_name, &deserialize_topic_len, &deserialize_payload,
+ &deserialize_payload_len),
+ MQTT_ERR_INVALID_PACKET_TYPE);
+}
+
+/**
+ * @brief Test puback packet.
+ *
+ */
+TEST(MQTTPacketTest, PUBACK) {
+ uint8_t test_packet[] = {0x40, 0x02, 0x70, 0xaf};
+
+ uint8_t packet_buf[128];
+
+ uint16_t serialize_packet_id = 28847;
+ ASSERT_EQ(mqtt_puback_packet_serialize(packet_buf, sizeof(packet_buf), serialize_packet_id), 4);
+ ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
+
+ uint16_t deserialize_packet_id;
+ ASSERT_EQ(mqtt_puback_packet_deserialize(test_packet, sizeof(test_packet), &deserialize_packet_id),
+ MQTT_RET_PACKET_OK);
+ ASSERT_EQ(deserialize_packet_id, serialize_packet_id);
+}
+
+/**
+ * @brief Test subscribe packet.
+ *
+ */
+TEST(MQTTPacketTest, SUBSCRIBE) {
+ uint8_t test_packet[] = {
+ 0x82, 0x19, 0x70, 0xae, 0x00, 0x14, 0x4a, 0x4e, 0x4c, 0x52, 0x57, 0x4f, 0x33, 0x54,
+ 0x35, 0x39, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x00,
+ };
+
+ uint8_t packet_buf[128];
+
+ std::string topic = "JNLRWO3T59/test/data";
+
+ uint16_t serialize_packet_id = 28846;
+ char *serialize_topic_name = reinterpret_cast(const_cast(topic.c_str()));
+ int serialize_qos = 0;
+
+ ASSERT_EQ(mqtt_subscribe_packet_serialize(packet_buf, sizeof(packet_buf), serialize_packet_id, 1,
+ &serialize_topic_name, &serialize_qos),
+ sizeof(test_packet));
+ ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
+}
+
+/**
+ * @brief Test suback packet.
+ *
+ */
+TEST(MQTTPacketTest, SUBACK) {
+ uint8_t test_packet[] = {0x90, 0x03, 0x70, 0xae, 0x00};
+
+ int sub_count = 0;
+ uint16_t packet_id;
+ int granted_qos[1];
+ ASSERT_EQ(mqtt_suback_packet_deserialize(test_packet, sizeof(test_packet), 1, &sub_count, &packet_id, granted_qos),
+ MQTT_RET_PACKET_OK);
+ ASSERT_EQ(sub_count, 1);
+ ASSERT_EQ(packet_id, 28846);
+ ASSERT_EQ(granted_qos[0], 0);
+}
+
+/**
+ * @brief Test unsubscribe packet.
+ *
+ */
+TEST(MQTTPacketTest, UNSUBSCRIBE) {
+ uint8_t test_packet[] = {
+ 0xa2, 0x18, 0x35, 0x74, 0x00, 0x14, 0x4a, 0x4e, 0x4c, 0x52, 0x57, 0x4f, 0x33,
+ 0x54, 0x35, 0x39, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x64, 0x61, 0x74, 0x61,
+ };
+
+ uint8_t packet_buf[128];
+ uint16_t packet_id = 13684;
+ std::string topic = "JNLRWO3T59/test/data";
+ char *topic_name = reinterpret_cast(const_cast(topic.c_str()));
+
+ ASSERT_EQ(mqtt_unsubscribe_packet_serialize(packet_buf, sizeof(packet_buf), packet_id, 1, &topic_name),
+ sizeof(test_packet));
+ ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
+}
+
+/**
+ * @brief Test connack packet.
+ *
+ */
+TEST(MQTTPacketTest, UNSUBACK) {
+ uint8_t test_packet[] = {0xb0, 0x02, 0x92, 0x6b};
+
+ uint16_t packet_id;
+
+ ASSERT_EQ(mqtt_unsuback_packet_deserialize(test_packet, sizeof(test_packet), &packet_id), MQTT_RET_PACKET_OK);
+ ASSERT_EQ(packet_id, 37483);
+}
+
+/**
+ * @brief Test pingreq packet.
+ *
+ */
+TEST(MQTTPacketTest, PINGREQ) {
+ uint8_t test_packet[] = {0xc0, 0x00};
+
+ uint8_t packet_buf[128];
+ ASSERT_EQ(mqtt_pingreq_packet_serialize(packet_buf, sizeof(packet_buf)), sizeof(test_packet));
+ ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
+}
+
+/**
+ * @brief Test pingresp packet.
+ *
+ */
+TEST(MQTTPacketTest, PINGRESP) {
+ uint8_t test_packet[] = {0xd0, 0x00};
+
+ ASSERT_EQ(mqtt_pingresp_packet_deserialize(test_packet, sizeof(test_packet)), MQTT_RET_PACKET_OK);
+}
+
+/**
+ * @brief Test disconnect packet.
+ *
+ */
+TEST(MQTTPacketTest, DISCONNECT) {
+ uint8_t test_packet[] = {0xe0, 0x00};
+
+ uint8_t packet_buf[128];
+ ASSERT_EQ(mqtt_disconnect_packet_serialize(packet_buf, sizeof(packet_buf)), sizeof(test_packet));
+ ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
+}
+
+} // namespace mqtt_packet_unittest
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/utils/CMakeLists.txt b/components/connectivity/iot-hub-device-c-sdk/common/utils/CMakeLists.txt
new file mode 100644
index 00000000..c46641a6
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/utils/CMakeLists.txt
@@ -0,0 +1,10 @@
+file(GLOB src ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
+set(inc ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
+
+set(src_common ${src_common} ${src} PARENT_SCOPE)
+set(inc_common ${inc_common} ${inc} PARENT_SCOPE)
+
+if( ${CONFIG_IOT_TEST} STREQUAL "ON")
+ file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
+ set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
+endif()
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/utils/inc/utils_downloader.h b/components/connectivity/iot-hub-device-c-sdk/common/utils/inc/utils_downloader.h
new file mode 100644
index 00000000..54bb06d0
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/utils/inc/utils_downloader.h
@@ -0,0 +1,104 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file utils_downloader.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-10-20
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-10-20 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_DOWNLOADER_H_
+#define IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_DOWNLOADER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+/**
+ * @brief Downloader status.
+ *
+ */
+typedef enum {
+ UTILS_DOWNLOADER_STATUS_SUCCESS = 0,
+ UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED,
+ UTILS_DOWNLOADER_STATUS_DATA_DOWNLOAD_FAILED,
+ UTILS_DOWNLOADER_STATUS_NETWORK_FAILED
+} UtilsDownloaderStatus;
+
+/**
+ * @brief Downloader function.
+ *
+ */
+typedef struct {
+ // memory
+ void* (*downloader_malloc)(size_t len); /**< user malloc */
+ void (*downloader_free)(void* val); /**< user free */
+
+ // break point
+ int (*break_point_init)(void* usr_data); /**< init break point, read from flash or file */
+ void (*break_point_deinit)(void* usr_data); /**< deinit break point */
+ int (*break_point_set)(void* usr_data); /**< set break point structure */
+ int (*break_point_save)(void* usr_data); /**< save break point in flash or file */
+ int (*break_point_check)(void* usr_data); /**< check break point valid */
+ int (*break_point_restore)(void* usr_data); /**< restore break point */
+
+ // data download
+ int (*data_download_init)(void* usr_data); /**< init data download, such as http connect */
+ void (*data_download_deinit)(void* usr_data); /**< deinit data download, such as http disconnect */
+
+ int (*data_download_is_over)(void* usr_data); /**< check if download finish */
+ int (*data_download_recv)(void* usr_data); /**< recv data, such as http recv */
+ int (*data_download_save)(void* usr_data); /**< save data, such as write firmware to flash */
+ int (*data_download_finish)(void* usr_data, UtilsDownloaderStatus status); /**< process result */
+} UtilsDownloaderFunction;
+
+/**
+ * @brief Init downloader.
+ *
+ * @param[in] func download function should implement
+ * @param[in] usr_data user data using in function
+ * @return pointer to downloader
+ */
+void* utils_downloader_init(UtilsDownloaderFunction func, void* usr_data);
+
+/**
+ * @brief Process download using function.
+ *
+ * @param[in,out] handle pointer to downloader
+ * @return -1 for fail, others see data_download_finish
+ */
+int utils_downloader_process(void* handle);
+
+/**
+ * @brief Deinit downloader.
+ *
+ * @param[in,out] handle pointer to downloader
+ */
+void utils_downloader_deinit(void* handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_DOWNLOADER_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/utils/inc/utils_json.h b/components/connectivity/iot-hub-device-c-sdk/common/utils/inc/utils_json.h
new file mode 100644
index 00000000..60297677
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/utils/inc/utils_json.h
@@ -0,0 +1,146 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file utils_json.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-07-24
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-07-24 1.0 fancyxu first commit
+ * 2021-07-29 1.1 fancyxu fix bug and add utils_json_value_data_get
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_JSON_H_
+#define IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_JSON_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+/**
+ * @brief Json value type
+ *
+ */
+typedef enum {
+ UTILS_JSON_VALUE_TYPE_INT32 = 0,
+ UTILS_JSON_VALUE_TYPE_INT64,
+ UTILS_JSON_VALUE_TYPE_UINT32,
+ UTILS_JSON_VALUE_TYPE_UINT64,
+ UTILS_JSON_VALUE_TYPE_FLOAT,
+ UTILS_JSON_VALUE_TYPE_DOUBLE,
+ UTILS_JSON_VALUE_TYPE_BOOLEAN,
+} UtilsJsonValueType;
+
+/**
+ * @brief Json array result
+ *
+ */
+typedef enum {
+ UTILS_JSON_ARRAY_ITER_CONTINUE,
+ UTILS_JSON_ARRAY_ITER_STOP,
+} UtilsJsonArrayIterResult;
+
+/**
+ * @brief Json value
+ *
+ */
+typedef struct {
+ const char *value;
+ int value_len;
+} UtilsJsonValue;
+
+/**
+ * @brief Get value from json string. Not strict, just for iot scene, we suppose all the string is valid json.
+ *
+ * @param[in] key key in json, support nesting with '.'
+ * @param[in] key_len key len
+ * @param[in] src json string
+ * @param[in] src_len src length
+ * @param[out] value value
+ * @return 0 for success
+ */
+int utils_json_value_get(const char *key, int key_len, const char *src, int src_len, UtilsJsonValue *value);
+
+/**
+ * @brief Get data of value with type.
+ *
+ * @param[in] value @see UtilsJsonValue
+ * @param[in] type value type, string can use value directly @see UtilsJsonValueType
+ * @param[out] data data pointer, user should match the type
+ * @return 0 for success
+ */
+int utils_json_value_data_get(UtilsJsonValue value, UtilsJsonValueType type, void *data);
+
+/**
+ * @brief Return unsigned int value of key in json.
+ *
+ * @param[in] key key in json, support nesting with '.'
+ * @param[in] key_len key len
+ * @param[in] src json string
+ * @param[in] src_len src length
+ * @param[out] data data value
+ * @return 0 for success
+ */
+int utils_json_get_uint32(const char *key, int key_len, const char *src, int src_len, uint32_t *data);
+
+/**
+ * @brief Return int value of key in json.
+ *
+ * @param[in] key key in json, support nesting with '.'
+ * @param[in] key_len key len
+ * @param[in] src json string
+ * @param[in] src_len src length
+ * @param[out] data data value
+ * @return 0 for success
+ */
+int utils_json_get_int32(const char *key, int key_len, const char *src, int src_len, int32_t *data);
+
+/**
+ * @brief Remove '\\' in json string.
+ *
+ * @param[in,out] src string to transfer
+ * @param[in] src_len string len
+ * @return length after transfer
+ */
+int utils_json_strip_transfer(char *src, int src_len);
+
+/**
+ * @brief Parse array object, assume array json is legal, src should be like "[12, 456]", this function will split array
+ * according to array_elem_type, obj_cb will be called for each elements.
+ *
+ * @param[in] src array string
+ * @param[in] src_len length of src
+ * @param[in] obj_cb callback to deal with array element
+ * @param[in] arg argument passed to the obj_cb
+ */
+void utils_json_array_parse(const char *src, int src_len, UtilsJsonArrayIterResult (*obj_cb)(const char *, int, void *),
+ void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_JSON_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/utils/inc/utils_list.h b/components/connectivity/iot-hub-device-c-sdk/common/utils/inc/utils_list.h
new file mode 100644
index 00000000..6a354484
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/utils/inc/utils_list.h
@@ -0,0 +1,156 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file utils_list.h
+ * @brief header file for utils list
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-25
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-25 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LIST_H_
+#define IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LIST_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+
+/**
+ * @brief ListNode iterator direction.
+ *
+ */
+typedef enum {
+ LIST_HEAD,
+ LIST_TAIL,
+} UtilsListDirection;
+
+/**
+ * @brief ListNode process result of OnNodeProcessHandle.
+ *
+ */
+typedef enum {
+ LIST_TRAVERSE_CONTINUE,
+ LIST_TRAVERSE_BREAK,
+} UtilsListResult;
+
+/**
+ * @brief Utils list function.
+ *
+ */
+typedef struct {
+ void *(*list_malloc)(size_t len);
+ void (*list_free)(void *val);
+
+ void *(*list_lock_init)(void);
+ void (*list_lock)(void *lock);
+ void (*list_unlock)(void *lock);
+ void (*list_lock_deinit)(void *lock);
+} UtilsListFunc;
+
+/**
+ * @brief Default list func
+ *
+ */
+#define DEFAULT_LIST_FUNCS \
+ { \
+ HAL_Malloc, HAL_Free, HAL_MutexCreate, HAL_MutexLock, HAL_MutexUnlock, HAL_MutexDestroy \
+ }
+
+/**
+ * @brief Node process handle called by utils_list_process.
+ *
+ */
+typedef UtilsListResult (*OnNodeProcessHandle)(void *list, void *node, void *val, void *usr_data);
+
+/**
+ * @brief Create list with max len, return NULL if fail.
+ *
+ * @param[in] func function needed by list
+ * @param[in] max_len max_len of list
+ * @return pointer to list, NULL for failed
+ */
+void *utils_list_create(UtilsListFunc func, int max_len);
+
+/**
+ * @brief Destroy list.
+ *
+ * @param[in] list pointer to list
+ */
+void utils_list_destroy(void *list);
+
+/**
+ * @brief Clear the list
+ *
+ * @param list
+ */
+void utils_list_clear(void *list);
+
+/**
+ * @brief Get list len.
+ *
+ * @param[in] list pointer to list
+ * @return len of list
+ */
+int utils_list_len_get(void *list);
+
+/**
+ * @brief Push the node to list tail, return NULL if node invalid.
+ *
+ * @param[in] list pointer to list
+ * @param[in] val value needed to push to list
+ * @return pointer to node, NULL for failed
+ */
+void *utils_list_push(void *list, void *val);
+
+/**
+ * @brief Pop the val from list head, return NULL if list empty.
+ *
+ * @param[in] list pointer to list
+ * @return val in the head node
+ */
+void *utils_list_pop(void *list);
+
+/**
+ * @brief Delete the node in list and release the resource.
+ *
+ * @param[in] list pointer to list
+ * @param[in] node pointer to node needed remove
+ */
+void utils_list_remove(void *list, void *node);
+
+/**
+ * @brief Process list using handle function.
+ *
+ * @param[in] list pointer to list
+ * @param[in] direction direction to traverse @see UtilsListDirection
+ * @param[in] handle process function @see OnNodeProcessHandle
+ * @param[in,out] usr_data usr data to pass to OnNodeProcessHandle
+ */
+void utils_list_process(void *list, UtilsListDirection direction, OnNodeProcessHandle handle, void *usr_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LIST_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/utils/inc/utils_log.h b/components/connectivity/iot-hub-device-c-sdk/common/utils/inc/utils_log.h
new file mode 100644
index 00000000..1cdbf88d
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/utils/inc/utils_log.h
@@ -0,0 +1,131 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file utils_log.h
+ * @brief header file for utils log
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-28
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-28 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LOG_H_
+#define IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LOG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#include
+#include
+
+/**
+ * @brief SDK log print/upload level.
+ *
+ */
+typedef enum {
+ LOG_LEVEL_DISABLE = 0, /**< disable log print/upload */
+ LOG_LEVEL_ERROR = 1, /**< error log level */
+ LOG_LEVEL_WARN = 2, /**< warning log level */
+ LOG_LEVEL_INFO = 3, /**< info log level */
+ LOG_LEVEL_DEBUG = 4, /**< debug log level */
+} LogLevel;
+
+/**
+ * @brief User's self defined log handler callback.
+ *
+ */
+typedef struct {
+ void *(*log_malloc)(size_t len);
+ void (*log_free)(void *val);
+ void (*log_handle)(const char *message);
+ void (*log_upload)(LogLevel log_level, const char *message);
+ void (*log_printf)(const char *fmt, ...);
+ char *(*log_get_current_time_str)(void);
+ void *(*log_mutex_create)(void);
+ void (*log_mutex_lock)(void *mutex);
+ void (*log_mutex_unlock)(void *mutex);
+ void (*log_mutex_destroy)(void *mutex);
+} LogHandleFunc;
+
+/**
+ * @brief Default log func
+ *
+ */
+#define DEFAULT_LOG_HANDLE_FUNCS \
+ { \
+ HAL_Malloc, HAL_Free, NULL, NULL, HAL_Printf, HAL_Timer_Current, HAL_MutexCreate, HAL_MutexLock, \
+ HAL_MutexUnlock, HAL_MutexDestroy \
+ }
+
+/**
+ * @brief Init log with func, log level, max log size.
+ *
+ * @param[in] func function should be implement for utils log
+ * @param[in] log_level @see LogLevel
+ * @param[in] max_log_size max size of log to print
+ * @return 0 for success
+ */
+int utils_log_init(LogHandleFunc func, LogLevel log_level, int max_log_size);
+
+/**
+ * @brief Deinit log.
+ *
+ */
+void utils_log_deinit(void);
+
+/**
+ * @brief Set log level.
+ *
+ * @param log_level @see LogLevel
+ */
+void utils_log_set_level(LogLevel log_level);
+
+/**
+ * @brief Get log level.
+ *
+ * @return @see LogLevel
+ */
+LogLevel utils_log_get_level(void);
+
+/**
+ * @brief Generate log if level higher than set.
+ *
+ * @param[in] file file path
+ * @param[in] func function where generate log
+ * @param[in] line line of source file where genertate log
+ * @param[in] level @see LogLevel
+ * @param[in] fmt format of log content
+ */
+void utils_log_gen(const char *file, const char *func, const int line, const int level, const char *fmt, ...);
+
+// Simple APIs for log generation in different level
+#define Log_d(fmt, ...) utils_log_gen(__FILE__, __FUNCTION__, __LINE__, LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__)
+#define Log_i(fmt, ...) utils_log_gen(__FILE__, __FUNCTION__, __LINE__, LOG_LEVEL_INFO, fmt, ##__VA_ARGS__)
+#define Log_w(fmt, ...) utils_log_gen(__FILE__, __FUNCTION__, __LINE__, LOG_LEVEL_WARN, fmt, ##__VA_ARGS__)
+#define Log_e(fmt, ...) utils_log_gen(__FILE__, __FUNCTION__, __LINE__, LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LOG_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/utils/src/utils_downloader.c b/components/connectivity/iot-hub-device-c-sdk/common/utils/src/utils_downloader.c
new file mode 100644
index 00000000..eb2a00d1
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/utils/src/utils_downloader.c
@@ -0,0 +1,142 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file utils_downloader.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-10-18
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-10-18 1.0 fancyxu first commit
+ *
+ */
+
+#include "utils_downloader.h"
+
+/**
+ * @brief Downloader.
+ *
+ */
+typedef struct {
+ UtilsDownloaderFunction func;
+ void* usr_data;
+} UtilsDownloader;
+
+/**
+ * @brief Init downloader.
+ *
+ * @param[in] func download function should implement
+ * @param[in] usr_data user data using in function
+ * @return pointer to downloader
+ */
+void* utils_downloader_init(UtilsDownloaderFunction func, void* usr_data)
+{
+ if (!func.downloader_malloc) {
+ return NULL;
+ }
+
+ UtilsDownloader* handle = func.downloader_malloc(sizeof(UtilsDownloader));
+
+ if (!handle) {
+ return NULL;
+ }
+
+ handle->func = func;
+ handle->usr_data = usr_data;
+ return handle;
+}
+
+/**
+ * @brief Process download using function.
+ *
+ * @param[in,out] handle pointer to downloader
+ * @return -1 for fail, others see data_download_finish
+ */
+int utils_downloader_process(void* handle)
+{
+ int rc = -1;
+
+ UtilsDownloader* downloader = handle;
+ UtilsDownloaderStatus status = UTILS_DOWNLOADER_STATUS_SUCCESS;
+
+ if (!handle) {
+ return -1;
+ }
+
+ rc = downloader->func.break_point_init(downloader->usr_data);
+ if (rc) {
+ status = UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED;
+ goto exit;
+ }
+
+ // if check ok, restore from break point, otherwise set break point
+ rc = downloader->func.break_point_check(downloader->usr_data)
+ ? downloader->func.break_point_set(downloader->usr_data)
+ : downloader->func.break_point_restore(downloader->usr_data);
+ if (rc) {
+ status = UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED;
+ goto exit;
+ }
+
+ rc = downloader->func.data_download_init(downloader->usr_data);
+ if (rc) {
+ status = UTILS_DOWNLOADER_STATUS_DATA_DOWNLOAD_FAILED;
+ goto exit;
+ }
+
+ while (!downloader->func.data_download_is_over(downloader->usr_data)) {
+ rc = downloader->func.data_download_recv(downloader->usr_data);
+ if (rc < 0) {
+ status = UTILS_DOWNLOADER_STATUS_NETWORK_FAILED;
+ goto exit;
+ }
+
+ rc = downloader->func.data_download_save(downloader->usr_data);
+ if (rc) {
+ status = UTILS_DOWNLOADER_STATUS_DATA_DOWNLOAD_FAILED;
+ goto exit;
+ }
+
+ rc = downloader->func.break_point_save(downloader->usr_data);
+ if (rc) {
+ status = UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED;
+ goto exit;
+ }
+ }
+
+exit:
+ rc = downloader->func.data_download_finish(downloader->usr_data, status);
+ downloader->func.break_point_deinit(downloader->usr_data);
+ downloader->func.data_download_deinit(downloader->usr_data);
+ return rc;
+}
+
+/**
+ * @brief Deinit downloader.
+ *
+ * @param[in,out] handle pointer to downloader
+ */
+void utils_downloader_deinit(void* handle)
+{
+ UtilsDownloader* downloader = handle;
+
+ if (!handle) {
+ return;
+ }
+ downloader->func.downloader_free(handle);
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/utils/src/utils_json.c b/components/connectivity/iot-hub-device-c-sdk/common/utils/src/utils_json.c
new file mode 100644
index 00000000..adb2ca1d
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/utils/src/utils_json.c
@@ -0,0 +1,684 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file utils_json.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-07-25
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-07-25 1.0 fancyxu first commit
+ * 2021-07-29 1.1 fancyxu fix bug and add utils_json_value_data_get
+ *
+ */
+
+#include "utils_json.h"
+
+/**
+ * @brief Delimiter of json
+ *
+ */
+typedef enum {
+ JSON_DELIMITER_ARRAY_BEGIN = '[',
+ JSON_DELIMITER_ARRAY_END = ']',
+ JSON_DELIMITER_OBJECT_BEGIN = '{',
+ JSON_DELIMITER_OBJECT_END = '}',
+ JSON_DELIMITER_KEY = '"',
+ JSON_DELIMITER_VALUE = ':',
+ JSON_DELIMITER_ELEMENT_END = ',',
+ JSON_DELIMITER_TYPE_STRING = '"',
+ JSON_DELIMITER_TYPE_BOOLEAN_TRUE_UPPER = 'T',
+ JSON_DELIMITER_TYPE_BOOLEAN_TRUE_LOWER = 't',
+ JSON_DELIMITER_TYPE_BOOLEAN_FALSE_UPPER = 'F',
+ JSON_DELIMITER_TYPE_BOOLEAN_FALSE_LOWER = 'f',
+ JSON_DELIMITER_TYPE_NULL_UPPER = 'N',
+ JSON_DELIMITER_TYPE_NULL_LOWER = 'n',
+ JSON_DELIMITER_SPACE = ' ',
+ JSON_DELIMITER_NONE = 0,
+} JsonDelimiter;
+
+/**
+ * @brief Element type support
+ *
+ */
+typedef enum {
+ JSON_ELEMENT_ARRAY = 0,
+ JSON_ELEMENT_OBJECT,
+ JSON_ELEMENT_VALUE_STRING,
+ JSON_ELEMENT_VALUE_NUMBER,
+ JSON_ELEMENT_VALUE_BOOLEAN,
+ JSON_ELEMENT_VALUE_NULL,
+ JSON_ELEMENT_KEY,
+} JsonElementType;
+
+/**
+ * @brief Json Element
+ *
+ */
+typedef struct {
+ JsonElementType type;
+ char *pos_begin;
+ char *pos_end;
+ int element_len;
+} JsonElement;
+
+#define json_key JsonElement
+#define json_value JsonElement
+
+/**
+ * @brief Is delimiter begin or end matched
+ *
+ */
+typedef struct {
+ int (*is_begin_matched)(char *str);
+ int (*is_end_matched)(char *str, int *offset);
+} IsDelimiterMatched;
+
+/**
+ * @brief For array type, when meets '[', means depth++
+ *
+ * @param[in] str pointer to char
+ * @return true for matched
+ */
+static int _is_array_begin_matched(char *str)
+{
+ return *str == JSON_DELIMITER_ARRAY_BEGIN;
+}
+
+/**
+ * @brief For array type, when meets ']', means depth--
+ *
+ * @param[in] str pointer to char
+ * @param[out] offset offset for pointer to move
+ * @return true for matched
+ */
+static int _is_array_end_matched(char *str, int *offset)
+{
+ return *str == JSON_DELIMITER_ARRAY_END;
+}
+
+/**
+ * @brief For object type, when meets '{', means depth++
+ *
+ * @param[in] str pointer to char
+ * @return true for matched
+ */
+static int _is_object_begin_matched(char *str)
+{
+ return *str == JSON_DELIMITER_OBJECT_BEGIN;
+}
+
+/**
+ * @brief For object type, when meets '}', means depth--
+ *
+ * @param[in] str pointer to char
+ * @param[out] offset offset for pointer to move
+ * @return true for matched
+ */
+static int _is_object_end_matched(char *str, int *offset)
+{
+ return *str == JSON_DELIMITER_OBJECT_END;
+}
+
+/**
+ * @brief For string type, when meets '"', but not "\"", means the end
+ *
+ * @param[in] str pointer to char
+ * @param[out] offset offset for pointer to move
+ * @return true for matched
+ */
+static int _is_string_end_matched(char *str, int *offset)
+{
+ return *str == '"' && *(str - 1) != '\\';
+}
+
+/**
+ * @brief For number type, when meets not '0~9', '.' or 'f' for float, means the end
+ *
+ * @param[in] str pointer to char
+ * @param[out] offset offset for pointer to move
+ * @return true for matched
+ */
+static int _is_number_end_matched(char *str, int *offset)
+{
+ return (*str < '0' || *str > '9') && *str != '.' && *str != 'f' && *str != '-';
+}
+
+/**
+ * @brief For boolen type, when meets "true", "True", "false" or "False", means the end
+ *
+ * @param[in] str pointer to char
+ * @param[out] offset offset for pointer to move, -1 means error
+ * @return true for matched
+ */
+static int _is_boolean_end_matched(char *str, int *offset)
+{
+ *offset = !strncmp(str, "true", 4) || !strncmp(str, "TRUE", 4) ? 4 : *offset;
+ *offset = !strncmp(str, "false", 5) || !strncmp(str, "FALSE", 5) ? 5 : *offset;
+ if (*offset == 0) {
+ *offset = -1; // -1 means error
+ }
+ return 1;
+}
+
+/**
+ * @brief For boolen type, when meets "null", "NULL", means the end
+ *
+ * @param[in] str pointer to char
+ * @param[out] offset offset for pointer to move, -1 means error
+ * @return true for matched
+ */
+static int _is_null_end_matched(char *str, int *offset)
+{
+ *offset = !strncmp(str, "null", 4) || !strncmp(str, "NULL", 4) ? 4 : 0;
+ if (*offset == 0) {
+ *offset = -1; // -1 means error
+ }
+ return 1;
+}
+
+/**
+ * @brief Table of matched function of elements
+ *
+ */
+static IsDelimiterMatched sg_element_matched_func[] = {
+ {_is_array_begin_matched, _is_array_end_matched}, // JSON_ELEMENT_ARRAY
+ {_is_object_begin_matched, _is_object_end_matched}, // JSON_ELEMENT_OBJECT
+ {NULL, _is_string_end_matched}, // JSON_ELEMENT_VALUE_STRING
+ {NULL, _is_number_end_matched}, // JSON_ELEMENT_VALUE_NUMBER
+ {NULL, _is_boolean_end_matched}, // JSON_ELEMENT_VALUE_BOOLEAN
+ {NULL, _is_null_end_matched}, // JSON_ELEMENT_VALUE_NULL
+ {NULL, NULL}, // JSON_ELEMENT_KEY
+};
+
+/**
+ * @brief Increase pos and decrease remain len
+ *
+ * @param[in,out] pos current pos
+ * @param[in,out] len current len
+ */
+static void _increase_pos(char **pos, int *len)
+{
+ (*pos)++;
+ (*len)--;
+};
+
+/**
+ * @brief Find delimiter and return pos
+ *
+ * @param[in] ch delimiter character
+ * @param[in] spilt_char if 0 then spilt nothing but delimiter
+ * @param[in] str input string
+ * @param[in,out] remain_len remaining length
+ * @return NULL for failed
+ */
+static char *_find_json_delimiter(JsonDelimiter ch, char spilt_char, const char *str, int *remain_len)
+{
+ char *pos = NULL;
+ char *src = (char *)str;
+
+ while ((*remain_len) > 0 && src && *src) {
+ if ((!spilt_char && *src != ch) || (spilt_char && *src == spilt_char) || (*src == '\n')) {
+ _increase_pos(&src, remain_len);
+ continue;
+ }
+
+ pos = (*src == ch) ? src : 0;
+ break;
+ }
+
+ return pos;
+}
+
+/**
+ * @brief Find element end
+ *
+ * @param[in] is_begin_matched using in nesting
+ * @param[in] is_end_matched match end
+ * @param[in] str input string
+ * @param[in,out] remain_len remaining length
+ * @return NULL for failed
+ */
+static char *_find_json_value_element_end(int (*is_begin_matched)(char *), int (*is_end_matched)(char *, int *),
+ char *str, int *remain_len)
+{
+ int offset = 0;
+ int depth = 0;
+ char *pos = NULL;
+
+ while (str && *str) {
+ offset = 0;
+
+ if (*remain_len <= 0) {
+ return NULL;
+ }
+
+ if (is_begin_matched) {
+ if (is_begin_matched(str)) {
+ depth++;
+ }
+ }
+
+ if (is_end_matched(str, &offset)) {
+ if (!depth) {
+ if (offset < 0) {
+ break; // failed
+ }
+ pos = str + offset;
+ (*remain_len) -= offset;
+ break;
+ }
+
+ if (depth > 0) {
+ depth--;
+ }
+ }
+
+ str++;
+ (*remain_len)--;
+ }
+
+ return pos;
+}
+
+/**
+ * @brief Get json key from string
+ *
+ * @param[in] node_begin string begin
+ * @param[in,out] remain_len remaining length
+ * @param[out] key key node
+ * @return 0 for success
+ */
+static int _get_json_key(char *node_begin, int *remain_len, json_key *key)
+{
+ char *key_begin = _find_json_delimiter(JSON_DELIMITER_KEY, JSON_DELIMITER_SPACE, node_begin, remain_len);
+ if (!key_begin) {
+ return -1;
+ }
+ _increase_pos(&key_begin, remain_len);
+
+ char *key_end = _find_json_delimiter(JSON_DELIMITER_KEY, JSON_DELIMITER_NONE, key_begin, remain_len);
+ if (!key_end) {
+ return -1;
+ }
+
+ key->type = JSON_ELEMENT_KEY;
+ key->pos_begin = key_begin;
+ key->pos_end = key_end;
+ key->element_len = key_end - key_begin;
+ return 0;
+}
+
+/**
+ * @brief Get json value from key end
+ *
+ * @param[in] key_end end of key
+ * @param[in,out] remain_len remaining length
+ * @param[out] value value node
+ * @return 0 for success
+ */
+static int _get_json_value(char *key_end, int *remain_len, json_value *value)
+{
+ char *value_begin = _find_json_delimiter(JSON_DELIMITER_VALUE, JSON_DELIMITER_SPACE, key_end, remain_len);
+ if (!value_begin) {
+ return -1;
+ }
+ _increase_pos(&value_begin, remain_len);
+
+ // filter all the space
+ while (*value_begin == ' ') {
+ value_begin++;
+ (*remain_len)--;
+ if (*remain_len <= 0) {
+ return -1;
+ }
+ };
+
+ JsonElementType type;
+
+ char *value_end = NULL;
+
+ switch (*value_begin) {
+ case JSON_DELIMITER_ARRAY_BEGIN:
+ // should + 1 for escape first [
+ _increase_pos(&value_begin, remain_len);
+ type = JSON_ELEMENT_ARRAY;
+ break;
+ case JSON_DELIMITER_OBJECT_BEGIN:
+ // should + 1 for escape first {
+ _increase_pos(&value_begin, remain_len);
+ type = JSON_ELEMENT_OBJECT;
+ break;
+ case JSON_DELIMITER_TYPE_STRING:
+ // should + 1 for escape first "
+ _increase_pos(&value_begin, remain_len);
+ type = JSON_ELEMENT_VALUE_STRING;
+ break;
+ case JSON_DELIMITER_TYPE_BOOLEAN_TRUE_UPPER:
+ case JSON_DELIMITER_TYPE_BOOLEAN_TRUE_LOWER:
+ case JSON_DELIMITER_TYPE_BOOLEAN_FALSE_UPPER:
+ case JSON_DELIMITER_TYPE_BOOLEAN_FALSE_LOWER:
+ type = JSON_ELEMENT_VALUE_BOOLEAN;
+ break;
+ case JSON_DELIMITER_TYPE_NULL_UPPER:
+ case JSON_DELIMITER_TYPE_NULL_LOWER:
+ type = JSON_ELEMENT_VALUE_NULL;
+ break;
+ default:
+ type = JSON_ELEMENT_VALUE_NUMBER;
+ break;
+ }
+
+ value_end = _find_json_value_element_end(sg_element_matched_func[type].is_begin_matched,
+ sg_element_matched_func[type].is_end_matched, value_begin, remain_len);
+ if (!value_end) {
+ return -1;
+ }
+
+ int is_object_or_array = (type == JSON_ELEMENT_ARRAY || type == JSON_ELEMENT_OBJECT);
+ int is_object_or_array_or_string = (is_object_or_array || type == JSON_ELEMENT_VALUE_STRING);
+
+ value->type = type;
+ value->pos_begin = value_begin - is_object_or_array; // include '[' or '{'
+ value->element_len = value_end - value->pos_begin + is_object_or_array; // include ']' or '}'
+ value->pos_end = value_end + is_object_or_array_or_string; // filter '"' , ']' or '}'
+ return 0;
+}
+
+/**
+ * @brief Get value by key from json string
+ *
+ * @param[in] str json string
+ * @param[in] str_len strign length
+ * @param[in] key key
+ * @param[in] key_len key length
+ * @param[out] value value
+ * @return 0 for success
+ */
+int json_get_value_by_key(const char *str, int str_len, const char *key, int key_len, json_value *value)
+{
+ int rc = 0;
+ char *object_begin = NULL;
+ json_key key_found = {JSON_ELEMENT_KEY, 0, 0, 0};
+
+ object_begin = _find_json_delimiter(JSON_DELIMITER_OBJECT_BEGIN, JSON_DELIMITER_SPACE, (char *)str, &str_len);
+ if (!object_begin) {
+ return -1;
+ }
+ _increase_pos(&object_begin, &str_len);
+
+ do {
+ rc = _get_json_key(object_begin, &str_len, &key_found);
+ if (rc) {
+ break;
+ }
+ _increase_pos(&key_found.pos_end, &str_len);
+
+ rc = _get_json_value(key_found.pos_end, &str_len, value);
+ if (rc) {
+ break;
+ }
+
+ // NULL of object begin can be ok for reach the end of str
+ object_begin = _find_json_delimiter(JSON_DELIMITER_ELEMENT_END, JSON_DELIMITER_SPACE, value->pos_end, &str_len);
+ if (object_begin) {
+ _increase_pos(&object_begin, &str_len);
+ }
+ } while (key_len != key_found.element_len || strncmp(key, key_found.pos_begin, key_found.element_len));
+
+ return rc;
+}
+
+/**
+ * @brief Get value from json string. Not strict, just for iot scene, we suppose all the string is valid json.
+ *
+ * @param[in] key key in json, support nesting with '.'
+ * @param[in] key_len key len
+ * @param[in] src json string
+ * @param[in] src_len src length
+ * @param[out] value value
+ * @return 0 for success
+ */
+int utils_json_value_get(const char *key, int key_len, const char *src, int src_len, UtilsJsonValue *value)
+{
+ int rc = 0;
+
+ char *delim = NULL;
+ char *src_iter = (char *)src;
+ int src_iter_len = src_len;
+ char *key_iter = (char *)key;
+ char *key_next = NULL;
+ int key_next_len = 0;
+
+ json_value value_tmp;
+
+ // key can be separated by '.', such as: outer_key.(.......).inner_key
+ while ((delim = strchr(key_iter, '.')) != NULL) {
+ key_next = key_iter;
+ key_next_len = delim - key_iter;
+
+ rc = json_get_value_by_key(src_iter, src_iter_len, key_next, key_next_len, &value_tmp);
+ if (rc) {
+ return rc;
+ }
+
+ src_iter = value_tmp.pos_begin;
+ src_iter_len = value_tmp.element_len;
+ key_iter = delim + 1;
+ }
+
+ // found inner key and get value
+ rc = json_get_value_by_key(src_iter, src_iter_len, key_iter, key_len + key - key_iter, &value_tmp);
+ if (rc) {
+ return rc;
+ }
+
+ value->value = value_tmp.pos_begin;
+ value->value_len = value_tmp.element_len;
+ return 0;
+}
+
+/**
+ * @brief Get data of value with type.
+ *
+ * @param[in] value @see UtilsJsonValue
+ * @param[in] type value type, string can use value directly. @see UtilsJsonValueType
+ * @param[out] data data pointer, user should
+ * @return 0 for success
+ */
+int utils_json_value_data_get(UtilsJsonValue value, UtilsJsonValueType type, void *data)
+{
+ char value_tmp[32] = {0};
+
+ if (value.value_len > sizeof(value_tmp) || !value.value_len) {
+ return -1;
+ }
+
+ strncpy(value_tmp, value.value, value.value_len);
+
+ switch (type) {
+ case UTILS_JSON_VALUE_TYPE_INT32:
+ return !(sscanf(value_tmp, "%" SCNi32, (int32_t *)data) == 1);
+ case UTILS_JSON_VALUE_TYPE_INT64:
+ return !(sscanf(value_tmp, "%" SCNi64, (int64_t *)data) == 1);
+ case UTILS_JSON_VALUE_TYPE_UINT32:
+ return !(sscanf(value_tmp, "%" SCNu32, (uint32_t *)data) == 1);
+ case UTILS_JSON_VALUE_TYPE_UINT64:
+ return !(sscanf(value_tmp, "%" SCNu64, (uint64_t *)data) == 1);
+ case UTILS_JSON_VALUE_TYPE_FLOAT:
+ return !(sscanf(value_tmp, "%f", (float *)data) == 1);
+ case UTILS_JSON_VALUE_TYPE_DOUBLE:
+ return !(sscanf(value_tmp, "%lf", (double *)data) == 1);
+ case UTILS_JSON_VALUE_TYPE_BOOLEAN:
+ *(int *)data = strcmp(value_tmp, "false");
+ return 0;
+ default:
+ break;
+ }
+ return -1;
+}
+
+/**
+ * @brief Return unsigned int value of key in json.
+ *
+ * @param[in] key key in json, support nesting with '.'
+ * @param[in] key_len key len
+ * @param[in] src json string
+ * @param[in] src_len src length
+ * @param[out] data data value
+ * @return 0 for success
+ */
+int utils_json_get_uint32(const char *key, int key_len, const char *src, int src_len, uint32_t *data)
+{
+ int rc;
+ UtilsJsonValue tmp;
+
+ rc = utils_json_value_get(key, key_len, src, src_len, &tmp);
+ if (rc) {
+ return -1;
+ }
+
+ rc = utils_json_value_data_get(tmp, UTILS_JSON_VALUE_TYPE_UINT32, data);
+ if (rc) {
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * @brief Return int value of key in json.
+ *
+ * @param[in] key key in json, support nesting with '.'
+ * @param[in] key_len key len
+ * @param[in] src json string
+ * @param[in] src_len src length
+ * @param[out] data data value
+ * @return 0 for success
+ */
+int utils_json_get_int32(const char *key, int key_len, const char *src, int src_len, int32_t *data)
+{
+ int rc;
+ UtilsJsonValue tmp;
+
+ rc = utils_json_value_get(key, key_len, src, src_len, &tmp);
+ if (rc) {
+ return -1;
+ }
+
+ rc = utils_json_value_data_get(tmp, UTILS_JSON_VALUE_TYPE_INT32, data);
+ if (rc) {
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * @brief Remove '\\' in json string.
+ *
+ * @param[in,out] src string to transfer
+ * @param[in] src_len string len
+ * @return length after transfer
+ */
+int utils_json_strip_transfer(char *src, int src_len)
+{
+ char *src_tmp = src;
+ char *end = src + src_len;
+
+ while (src_tmp < end) {
+ if (*src_tmp == '\\') {
+ memmove(src_tmp, src_tmp + 1, end - src_tmp);
+ end--;
+ }
+ src_tmp++;
+ }
+ return end - src;
+}
+
+/**
+ * @brief Parse array object, assume array json is legal, src should be like "[12, 456]", this function will split array
+ * according to array_elem_type, obj_cb will be called for each elements.
+ *
+ * @param[in] src array string
+ * @param[in] src_len length of src
+ * @param[in] obj_cb callback to deal with array element
+ * @param[in] arg argument passed to the obj_cb
+ */
+void utils_json_array_parse(const char *src, int src_len, UtilsJsonArrayIterResult (*obj_cb)(const char *, int, void *),
+ void *arg)
+{
+#define SKIP_SPACE(str) while (*str == ' ' && (str = str + 1))
+
+ if (!obj_cb) {
+ return;
+ }
+
+ const char *start = src + 1, *end = src + 1;
+
+ char left_delimiter, right_delimiter;
+ SKIP_SPACE(start);
+ switch (*start) {
+ case JSON_DELIMITER_ARRAY_BEGIN:
+ left_delimiter = JSON_DELIMITER_ARRAY_BEGIN;
+ right_delimiter = JSON_DELIMITER_ARRAY_END;
+ break;
+ case JSON_DELIMITER_OBJECT_BEGIN:
+ left_delimiter = JSON_DELIMITER_OBJECT_BEGIN;
+ right_delimiter = JSON_DELIMITER_OBJECT_END;
+ break;
+ case JSON_DELIMITER_TYPE_STRING:
+ left_delimiter = JSON_DELIMITER_TYPE_STRING;
+ right_delimiter = JSON_DELIMITER_TYPE_STRING;
+ break;
+ default:
+ left_delimiter = 0;
+ right_delimiter = JSON_DELIMITER_ELEMENT_END;
+ break;
+ }
+
+ int remain_length = 0;
+ while (end && end <= (src + src_len)) {
+ SKIP_SPACE(start);
+ if (left_delimiter) {
+ remain_length = src + src_len - start;
+ start = _find_json_delimiter(left_delimiter, JSON_DELIMITER_NONE, start, &remain_length);
+ if (!start) {
+ break;
+ }
+ if (JSON_DELIMITER_TYPE_STRING == left_delimiter) {
+ start++;
+ }
+ }
+
+ remain_length = src + src_len - start;
+ end = _find_json_delimiter(right_delimiter, JSON_DELIMITER_NONE, start, &remain_length);
+ if (!end) {
+ if (!left_delimiter) {
+ obj_cb(start, src + src_len - start - 1, arg);
+ }
+ break;
+ }
+ if (JSON_DELIMITER_ARRAY_END == right_delimiter || JSON_DELIMITER_OBJECT_END == right_delimiter) {
+ end++;
+ }
+
+ if (UTILS_JSON_ARRAY_ITER_STOP == obj_cb(start, end - start, arg)) {
+ return;
+ }
+ start = end + 1;
+ }
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/utils/src/utils_list.c b/components/connectivity/iot-hub-device-c-sdk/common/utils/src/utils_list.c
new file mode 100644
index 00000000..9ff3b43c
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/utils/src/utils_list.c
@@ -0,0 +1,338 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file utils_list.c
+ * @brief utils list operation
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-25
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-28 1.0 fancyxu first commit
+ *
+ */
+
+#include "utils_list.h"
+
+/**
+ * @brief Define list node.
+ *
+ */
+typedef struct ListNode {
+ struct ListNode *prev;
+ struct ListNode *next;
+ void *val;
+} ListNode;
+
+/**
+ * @brief Double Linked List.
+ *
+ */
+typedef struct {
+ UtilsListFunc func;
+ ListNode *head;
+ ListNode *tail;
+ void *lock;
+ int len;
+ int max_len;
+} List;
+
+/**
+ * @brief List iterator.
+ *
+ */
+typedef struct {
+ List *list;
+ ListNode *next;
+ UtilsListDirection direction;
+} ListIterator;
+
+/**
+ * @brief Lock list.
+ *
+ * @param[in] list pointer to list
+ */
+static inline void _list_lock(List *list)
+{
+ if (list->lock) {
+ list->func.list_lock(list->lock);
+ }
+}
+
+/**
+ * @brief Unlock list.
+ *
+ * @param[in] list pointer to list
+ */
+static inline void _list_unlock(List *list)
+{
+ if (list->lock) {
+ list->func.list_unlock(list->lock);
+ }
+}
+
+/**
+ * @brief Delete the node in list and release the resource.
+ *
+ * @param[in] list pointer to list
+ * @param[in] node pointer to node needed remove
+ */
+static void _list_remove(void *list, void *node)
+{
+ List *self = (List *)list;
+ ListNode *list_node = (ListNode *)node;
+
+ list_node->prev ? (list_node->prev->next = list_node->next) : (self->head = list_node->next);
+
+ list_node->next ? (list_node->next->prev = list_node->prev) : (self->tail = list_node->prev);
+
+ self->func.list_free(list_node->val);
+ self->func.list_free(list_node);
+
+ if (self->len) {
+ --self->len;
+ }
+}
+
+/**
+ * @brief Create list with max len, return NULL if fail.
+ *
+ * @param[in] func function needed by list
+ * @param[in] max_len max_len of list
+ * @return pointer to list, NULL for failed
+ */
+void *utils_list_create(UtilsListFunc func, int max_len)
+{
+ List *self;
+
+ if (max_len <= 0) {
+ return NULL;
+ }
+
+ self = (List *)func.list_malloc(sizeof(List));
+ if (!self) {
+ return NULL;
+ }
+
+ memset(self, 0, sizeof(List));
+
+ if (func.list_lock_init) {
+ self->lock = func.list_lock_init();
+ if (!self->lock) {
+ func.list_free(self);
+ return NULL;
+ }
+ }
+
+ self->func = func;
+ self->max_len = max_len;
+ return self;
+}
+
+/**
+ * @brief Destroy list.
+ *
+ * @param[in] list pointer to list
+ */
+void utils_list_destroy(void *list)
+{
+ utils_list_clear(list);
+ List *self = (List *)list;
+ if (self->lock) {
+ self->func.list_lock_deinit(self->lock);
+ }
+ self->func.list_free(self);
+}
+
+/**
+ * @brief Clear the list.
+ *
+ * @param[in] list pointer to list
+ */
+void utils_list_clear(void *list)
+{
+ if (!list) {
+ return;
+ }
+
+ List *self = (List *)list;
+
+ _list_lock(self);
+
+ ListNode *next;
+ ListNode *curr = self->head;
+
+ while (self->len--) {
+ next = curr->next;
+ self->func.list_free(curr->val);
+ self->func.list_free(curr);
+ curr = next;
+ }
+
+ _list_unlock(self);
+}
+
+/**
+ * @brief Get list len.
+ *
+ * @param[in] list pointer to list
+ * @return len of list
+ */
+int utils_list_len_get(void *list)
+{
+ List *self = (List *)list;
+ return self->len;
+}
+
+/**
+ * @brief Push the node to list tail, return NULL if node invalid.
+ *
+ * @param[in] list pointer to list
+ * @param[in] val value needed to push to list
+ * @return pointer to node, NULL for failed
+ */
+void *utils_list_push(void *list, void *val)
+{
+ List *self = (List *)list;
+
+ _list_lock(self);
+
+ if (!val || self->len >= self->max_len) {
+ _list_unlock(self);
+ return NULL;
+ }
+
+ ListNode *node;
+ node = self->func.list_malloc(sizeof(ListNode));
+ if (!node) {
+ _list_unlock(self);
+ return NULL;
+ }
+
+ node->prev = NULL;
+ node->next = NULL;
+ node->val = val;
+
+ if (self->len) {
+ node->prev = self->tail;
+ node->next = NULL;
+ self->tail->next = node;
+ self->tail = node;
+ } else {
+ self->head = self->tail = node;
+ node->prev = node->next = NULL;
+ }
+
+ ++self->len;
+
+ _list_unlock(self);
+ return node;
+}
+
+/**
+ * @brief Pop the value from list head, return NULL if list empty.
+ *
+ * @param[in] list pointer to list
+ * @return value in the head node
+ */
+void *utils_list_pop(void *list)
+{
+ List *self = (List *)list;
+ ListNode *node = NULL;
+
+ _list_lock(self);
+
+ if (!self->len) {
+ _list_unlock(self);
+ return NULL;
+ }
+
+ node = self->head;
+
+ if (--self->len) {
+ (self->head = node->next)->prev = NULL;
+ } else {
+ self->head = self->tail = NULL;
+ }
+
+ node->next = node->prev = NULL;
+
+ _list_unlock(self);
+
+ void *val = node->val;
+ self->func.list_free(node);
+ return val;
+}
+
+/**
+ * @brief Delete the node in list and release the resource.
+ *
+ * @param[in] list pointer to list
+ * @param[in] node pointer to node needed remove
+ */
+void utils_list_remove(void *list, void *node)
+{
+ List *self = (List *)list;
+ _list_lock(self);
+ _list_remove(self, node);
+ _list_unlock(self);
+}
+
+/**
+ * @brief Process list using handle function.
+ *
+ * @param[in] list pointer to list
+ * @param[in] direction direction to traverse
+ * @param[in] handle process function @see OnNodeProcessHandle
+ * @param[in,out] usr_data usr data to pass to OnNodeProcessHandle
+ */
+void utils_list_process(void *list, UtilsListDirection direction, OnNodeProcessHandle handle, void *usr_data)
+{
+ int rc;
+ ListNode *node = NULL;
+ List *self = (List *)list;
+
+ _list_lock(self);
+
+ if (!utils_list_len_get(list)) {
+ _list_unlock(self);
+ return;
+ }
+
+ ListIterator iterator = {
+ .direction = direction,
+ .list = self,
+ .next = direction == LIST_HEAD ? self->head : self->tail,
+ };
+
+ // traverse list to process
+ while ((node = iterator.next) != NULL) {
+ iterator.next = iterator.direction == LIST_HEAD ? node->next : node->prev;
+ if (!node->val) {
+ _list_remove(list, node);
+ continue;
+ }
+ // process node and val
+ if (handle) {
+ rc = handle(list, node, node->val, usr_data);
+ if (rc) {
+ break;
+ }
+ }
+ }
+ _list_unlock(list);
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/utils/src/utils_log.c b/components/connectivity/iot-hub-device-c-sdk/common/utils/src/utils_log.c
new file mode 100644
index 00000000..765aeb47
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/utils/src/utils_log.c
@@ -0,0 +1,178 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file utils_log.c
+ * @brief different level log generator
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-28
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-28 1.0 fancyxu first commit
+ *
+ */
+
+#include "utils_log.h"
+
+static const char *LEVEL_STR[] = {"DIS", "ERR", "WRN", "INF", "DBG"};
+static LogHandleFunc sg_log_handle_func;
+static char *sg_log_buffer;
+static int sg_log_max_size;
+static void *sg_log_mutex;
+static LogLevel sg_log_print_level = LOG_LEVEL_DEBUG;
+extern LogLevel sg_log_upload_level;
+
+/**
+ * @brief Get file name form path.
+ *
+ * @param[in] path file path
+ * @return file name
+ */
+static const char *_get_filename(const char *path)
+{
+#ifdef WIN32
+ char ch = '\\';
+#else
+ char ch = '/';
+#endif
+ const char *q = strrchr(path, ch);
+ if (!q) {
+ q = path;
+ } else {
+ q++;
+ }
+ return q;
+}
+
+/**
+ * @brief Init log with func, log level, max log size.
+ *
+ * @param[in] func function should be implement for utils log
+ * @param[in] log_level @see LogLevel
+ * @param[in] max_log_size max size of log to print
+ * @return 0 for success
+ */
+int utils_log_init(LogHandleFunc func, LogLevel log_level, int max_log_size)
+{
+ sg_log_handle_func = func;
+ sg_log_print_level = log_level;
+ sg_log_max_size = max_log_size;
+ if (func.log_mutex_create) {
+ sg_log_mutex = func.log_mutex_create();
+ if (!sg_log_mutex) {
+ return -1;
+ }
+ }
+
+ if (func.log_malloc) {
+ sg_log_buffer = func.log_malloc(max_log_size);
+ if (!sg_log_buffer) {
+ if (sg_log_mutex) {
+ func.log_mutex_destroy(sg_log_mutex);
+ }
+ }
+ }
+ return !sg_log_buffer;
+}
+
+/**
+ * @brief Deinit log.
+ *
+ */
+void utils_log_deinit(void)
+{
+ sg_log_handle_func.log_free(sg_log_buffer);
+ if (sg_log_mutex) {
+ sg_log_handle_func.log_mutex_destroy(sg_log_mutex);
+ }
+ sg_log_mutex = NULL;
+ sg_log_buffer = NULL;
+}
+
+/**
+ * @brief Set log level.
+ *
+ * @param log_level @see LogLevel
+ */
+void utils_log_set_level(LogLevel log_level)
+{
+ sg_log_print_level = log_level;
+}
+
+/**
+ * @brief Get log level.
+ *
+ * @return @see LogLevel
+ */
+LogLevel utils_log_get_level(void)
+{
+ return sg_log_print_level;
+}
+
+/**
+ * @brief Generate log if level higher than set.
+ *
+ * @param[in] file file path
+ * @param[in] func function where generate log
+ * @param[in] line line of source file where genertate log
+ * @param[in] level @see LogLevel
+ * @param[in] fmt format of log content
+ */
+void utils_log_gen(const char *file, const char *func, const int line, const int level, const char *fmt, ...)
+{
+ if (level > sg_log_print_level) {
+ return;
+ }
+
+ if (sg_log_mutex) {
+ sg_log_handle_func.log_mutex_lock(sg_log_mutex);
+ }
+
+ /* format log content */
+ const char *file_name = _get_filename(file);
+ char *o = sg_log_buffer;
+
+ memset(sg_log_buffer, 0, sg_log_max_size);
+
+ o += snprintf(sg_log_buffer, sg_log_max_size, "%s|%s|%s|%s(%d): ", LEVEL_STR[level],
+ sg_log_handle_func.log_get_current_time_str(), file_name, func, line);
+
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(o, sg_log_max_size - 2 - strlen(sg_log_buffer), fmt, ap);
+ va_end(ap);
+
+ strncat(sg_log_buffer, "\r\n", sg_log_max_size - strlen(sg_log_buffer) - 1);
+
+ if (level <= sg_log_print_level) {
+ if (sg_log_handle_func.log_handle) {
+ sg_log_handle_func.log_handle(sg_log_buffer);
+ }
+ sg_log_handle_func.log_printf("%s", sg_log_buffer);
+ }
+
+ /* append to upload buffer */
+ if (sg_log_handle_func.log_upload) {
+ sg_log_handle_func.log_upload(level, sg_log_buffer);
+ }
+
+ if (sg_log_mutex) {
+ sg_log_handle_func.log_mutex_unlock(sg_log_mutex);
+ }
+
+ return;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/utils/test/.clang-format b/components/connectivity/iot-hub-device-c-sdk/common/utils/test/.clang-format
new file mode 100644
index 00000000..334c1c0d
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/utils/test/.clang-format
@@ -0,0 +1,8 @@
+Language: Cpp
+BasedOnStyle: Google
+ColumnLimit: 120
+DerivePointerAlignment: true
+PointerAlignment: Left
+SortIncludes: true
+IncludeBlocks: Preserve
+IndentPPDirectives: AfterHash
\ No newline at end of file
diff --git a/components/connectivity/iot-hub-device-c-sdk/common/utils/test/test_utils.cc b/components/connectivity/iot-hub-device-c-sdk/common/utils/test/test_utils.cc
new file mode 100644
index 00000000..6a03d99d
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/common/utils/test/test_utils.cc
@@ -0,0 +1,214 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file test_utils.cc
+ * @brief unittest for utils
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-07-07
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-07-07 1.0 fancyxu first commit
+ * 2021-07-27 1.1 fancyxu support utils json
+ * 2021-07-29 1.1 fancyxu rename HAL_Timer and add utils json test
+ *
+ */
+
+#include
+#include
+
+#include "gtest/gtest.h"
+#include "qcloud_iot_platform.h"
+#include "utils_json.h"
+#include "utils_list.h"
+#include "utils_log.h"
+
+namespace utils_unittest {
+
+/**
+ * @brief test fixture of utils list
+ *
+ */
+class UtilsListTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ UtilsListFunc func = DEFAULT_LIST_FUNCS;
+
+ self_list = utils_list_create(func, 10);
+ ASSERT_NE(self_list, nullptr);
+
+ for (int i = 0; i < 10;) {
+ int *val = reinterpret_cast(HAL_Malloc(sizeof(int)));
+ *val = i++;
+ ASSERT_NE(utils_list_push(self_list, reinterpret_cast(val)), nullptr);
+ ASSERT_EQ(utils_list_len_get(self_list), i);
+ }
+ }
+
+ void TearDown() override { utils_list_destroy(self_list); }
+
+ void *self_list;
+};
+
+/**
+ * @brief Test list.
+ *
+ */
+TEST_F(UtilsListTest, list) {
+ ASSERT_EQ(utils_list_push(self_list, reinterpret_cast(1)), nullptr);
+ for (int i = 0; i < 10; i++) {
+ ASSERT_EQ(utils_list_len_get(self_list), 10 - i);
+ int *val = reinterpret_cast(utils_list_pop(self_list));
+ ASSERT_EQ(*val, i);
+ HAL_Free(val);
+ }
+}
+
+/**
+ * @brief Test remove list node
+ *
+ * @param[in,out] list pointer to list
+ * @param[in,out] node pointer to node
+ * @param[in,out] val pointer to val
+ * @param[in,out] usr_data pointer to usr data
+ * @return @see UtilsListResult
+ */
+static UtilsListResult list_process_remove(void *list, void *node, void *val, void *usr_data) {
+ static int i = 0;
+ if (*reinterpret_cast(val) != i++) {
+ return LIST_TRAVERSE_BREAK;
+ }
+ utils_list_remove(list, node);
+ return LIST_TRAVERSE_CONTINUE;
+}
+
+/**
+ * @brief Test list process.
+ *
+ */
+TEST_F(UtilsListTest, list_process) {
+ utils_list_process(self_list, LIST_HEAD, list_process_remove, NULL);
+ ASSERT_EQ(utils_list_len_get(self_list), 0);
+}
+
+/**
+ * @brief Test log.
+ *
+ */
+TEST(UtilsLogTest, log) {
+ LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
+ ASSERT_EQ(utils_log_init(func, LOG_LEVEL_DEBUG, 2048), 0);
+ Log_d("Here is a debug level log test!");
+ Log_i("Here is a info level log test!");
+ Log_w("Here is a warning level log test!");
+ Log_e("Here is a error level log test!");
+ utils_log_deinit();
+}
+
+/**
+ * @brief Test json.
+ *
+ */
+TEST(UtilsJsonTest, json) {
+ char test_json[] =
+ "{\"str_test\":\"test\",\"int_test\":100,\"float_test\":1.210f,\"bool_test\":true,"
+ "\"bool_false_test\":false,\"null_test\":null}";
+
+ UtilsJsonValue value;
+
+ int data_int = 0;
+ ASSERT_EQ(utils_json_value_get("int_test", strlen("int_test"), test_json, strlen(test_json), &value), 0);
+ ASSERT_EQ(utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_INT32, &data_int), 0);
+ ASSERT_EQ(data_int, 100);
+
+ float data_float = 0.0;
+ ASSERT_EQ(utils_json_value_get("float_test", strlen("float_test"), test_json, strlen(test_json), &value), 0);
+ ASSERT_EQ(utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_FLOAT, &data_float), 0);
+ ASSERT_EQ(data_float, 1.210f);
+
+ ASSERT_EQ(utils_json_value_get("bool_test", strlen("bool_test"), test_json, strlen(test_json), &value), 0);
+ ASSERT_EQ(strncmp(value.value, "true", value.value_len), 0);
+
+ ASSERT_EQ(utils_json_value_get("bool_false_test", strlen("bool_false_test"), test_json, strlen(test_json), &value),
+ 0);
+ ASSERT_EQ(strncmp(value.value, "false", value.value_len), 0);
+
+ ASSERT_EQ(utils_json_value_get("null_test", strlen("null_test"), test_json, strlen(test_json), &value), 0);
+ ASSERT_EQ(strncmp(value.value, "null", value.value_len), 0);
+
+ ASSERT_EQ(utils_json_value_get("str_test", strlen("str_test"), test_json, strlen(test_json), &value), 0);
+ ASSERT_EQ(strncmp(value.value, "test", value.value_len), 0);
+
+ char test_json_depth[] = "{\"depth_test\": {\"test\":\"test1\"}}";
+ ASSERT_EQ(utils_json_value_get("depth_test.test", strlen("depth_test.test"), test_json_depth, strlen(test_json_depth),
+ &value),
+ 0);
+ ASSERT_EQ(strncmp(value.value, "test1", value.value_len), 0);
+
+ char test_json_before_strip[] =
+ "{\\\"str_test\":\\\"test\\\",\\\"int_test\\\":100,\\\"float_test\\\":1.210f,\\\"bool_test\\\":true,"
+ "\\\"bool_false_test\\\":false,\\\"null_test\\\":null}";
+ ASSERT_EQ(utils_json_strip_transfer(test_json_before_strip, strlen(test_json_before_strip)), strlen(test_json));
+ ASSERT_EQ(strncmp(test_json_before_strip, test_json, strlen(test_json_before_strip)), 0);
+
+ char test_json_break[] = "{\n\"str_test\":\"test\"\n}";
+ ASSERT_EQ(utils_json_value_get("str_test", strlen("str_test"), test_json_break, strlen(test_json_break), &value), 0);
+ ASSERT_EQ(strncmp(value.value, "test", value.value_len), 0);
+}
+
+/**
+ * @brief Test json array.
+ *
+ */
+static const char *sg_dst_value[12];
+
+static UtilsJsonArrayIterResult _array_handle(const char *value, int value_len, void *arg) {
+ static int i = 0;
+ EXPECT_EQ(0, strncmp(sg_dst_value[i++], value, value_len));
+ return UTILS_JSON_ARRAY_ITER_CONTINUE;
+}
+
+TEST(UtilsJsonTest, json_array) {
+ int i = 0;
+ char test_array_string[] = "[\"test0\",\"test1\"]";
+ sg_dst_value[i++] = "test0";
+ sg_dst_value[i++] = "test1";
+ char test_array_int[] = "[1,2]";
+ sg_dst_value[i++] = "1";
+ sg_dst_value[i++] = "2";
+ char test_array_float[] = "[1.20f,1.21f]";
+ sg_dst_value[i++] = "1.20f";
+ sg_dst_value[i++] = "1.21f";
+ char test_array_bool[] = "[false,true]";
+ sg_dst_value[i++] = "false";
+ sg_dst_value[i++] = "true";
+ char test_array_null[] = "[null,null]";
+ sg_dst_value[i++] = "null";
+ sg_dst_value[i++] = "null";
+ char test_array_obj[] = "[{\"test\":0},{\"test\":1}]";
+ sg_dst_value[i++] = "{\"test\":0}";
+ sg_dst_value[i++] = "{\"test\":1}";
+
+ utils_json_array_parse(test_array_string, sizeof(test_array_string) - 1, _array_handle, NULL);
+ utils_json_array_parse(test_array_int, sizeof(test_array_int) - 1, _array_handle, NULL);
+ utils_json_array_parse(test_array_float, sizeof(test_array_float) - 1, _array_handle, NULL);
+ utils_json_array_parse(test_array_bool, sizeof(test_array_bool) - 1, _array_handle, NULL);
+ utils_json_array_parse(test_array_null, sizeof(test_array_null) - 1, _array_handle, NULL);
+ utils_json_array_parse(test_array_obj, sizeof(test_array_obj) - 1, _array_handle, NULL);
+}
+
+} // namespace utils_unittest
diff --git a/components/connectivity/iot-hub-device-c-sdk/config/gcovr/code_coverage.cmake b/components/connectivity/iot-hub-device-c-sdk/config/gcovr/code_coverage.cmake
new file mode 100644
index 00000000..2784d032
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/config/gcovr/code_coverage.cmake
@@ -0,0 +1,687 @@
+# Copyright (c) 2012 - 2017, Lars Bilke
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors
+# may be used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# CHANGES:
+#
+# 2012-01-31, Lars Bilke
+# - Enable Code Coverage
+#
+# 2013-09-17, Joakim Söderberg
+# - Added support for Clang.
+# - Some additional usage instructions.
+#
+# 2016-02-03, Lars Bilke
+# - Refactored functions to use named parameters
+#
+# 2017-06-02, Lars Bilke
+# - Merged with modified version from github.com/ufz/ogs
+#
+# 2019-05-06, Anatolii Kurotych
+# - Remove unnecessary --coverage flag
+#
+# 2019-12-13, FeRD (Frank Dana)
+# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor
+# of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments.
+# - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY
+# - All setup functions: accept BASE_DIRECTORY, EXCLUDE list
+# - Set lcov basedir with -b argument
+# - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be
+# overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().)
+# - Delete output dir, .info file on 'make clean'
+# - Remove Python detection, since version mismatches will break gcovr
+# - Minor cleanup (lowercase function names, update examples...)
+#
+# 2019-12-19, FeRD (Frank Dana)
+# - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets
+#
+# 2020-01-19, Bob Apthorpe
+# - Added gfortran support
+#
+# 2020-02-17, FeRD (Frank Dana)
+# - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters
+# in EXCLUDEs, and remove manual escaping from gcovr targets
+#
+# 2021-01-19, Robin Mueller
+# - Add CODE_COVERAGE_VERBOSE option which will allow to print out commands which are run
+# - Added the option for users to set the GCOVR_ADDITIONAL_ARGS variable to supply additional
+# flags to the gcovr command
+#
+# 2020-05-04, Mihchael Davis
+# - Add -fprofile-abs-path to make gcno files contain absolute paths
+# - Fix BASE_DIRECTORY not working when defined
+# - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines
+#
+# 2021-05-10, Martin Stump
+# - Check if the generator is multi-config before warning about non-Debug builds
+#
+# USAGE:
+#
+# 1. Copy this file into your cmake modules path.
+#
+# 2. Add the following line to your CMakeLists.txt (best inside an if-condition
+# using a CMake option() to enable it just optionally):
+# include(CodeCoverage)
+#
+# 3. Append necessary compiler flags:
+# append_coverage_compiler_flags()
+#
+# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og
+#
+# 4. If you need to exclude additional directories from the report, specify them
+# using full paths in the COVERAGE_EXCLUDES variable before calling
+# setup_target_for_coverage_*().
+# Example:
+# set(COVERAGE_EXCLUDES
+# '${PROJECT_SOURCE_DIR}/src/dir1/*'
+# '/path/to/my/src/dir2/*')
+# Or, use the EXCLUDE argument to setup_target_for_coverage_*().
+# Example:
+# setup_target_for_coverage_lcov(
+# NAME coverage
+# EXECUTABLE testrunner
+# EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*")
+#
+# 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set
+# relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR)
+# Example:
+# set(COVERAGE_EXCLUDES "dir1/*")
+# setup_target_for_coverage_gcovr_html(
+# NAME coverage
+# EXECUTABLE testrunner
+# BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src"
+# EXCLUDE "dir2/*")
+#
+# 5. Use the functions described below to create a custom make target which
+# runs your test executable and produces a code coverage report.
+#
+# 6. Build a Debug build:
+# cmake -DCMAKE_BUILD_TYPE=Debug ..
+# make
+# make my_coverage_target
+#
+
+include(CMakeParseArguments)
+
+option(CODE_COVERAGE_VERBOSE "Verbose information" FALSE)
+
+# Check prereqs
+find_program( GCOV_PATH gcov )
+find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl)
+find_program( FASTCOV_PATH NAMES fastcov fastcov.py )
+find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat )
+find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
+find_program( CPPFILT_PATH NAMES c++filt )
+
+if(NOT GCOV_PATH)
+ message(FATAL_ERROR "gcov not found! Aborting...")
+endif() # NOT GCOV_PATH
+
+get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
+list(GET LANGUAGES 0 LANG)
+
+if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
+ if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3)
+ message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
+ endif()
+elseif(NOT CMAKE_COMPILER_IS_GNUCXX)
+ if("${CMAKE_Fortran_COMPILER_ID}" MATCHES "[Ff]lang")
+ # Do nothing; exit conditional without error if true
+ elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU")
+ # Do nothing; exit conditional without error if true
+ else()
+ message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
+ endif()
+endif()
+
+set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage"
+ CACHE INTERNAL "")
+if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
+ include(CheckCXXCompilerFlag)
+ check_cxx_compiler_flag(-fprofile-abs-path HAVE_fprofile_abs_path)
+ if(HAVE_fprofile_abs_path)
+ set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
+ endif()
+endif()
+
+set(CMAKE_Fortran_FLAGS_COVERAGE
+ ${COVERAGE_COMPILER_FLAGS}
+ CACHE STRING "Flags used by the Fortran compiler during coverage builds."
+ FORCE )
+set(CMAKE_CXX_FLAGS_COVERAGE
+ ${COVERAGE_COMPILER_FLAGS}
+ CACHE STRING "Flags used by the C++ compiler during coverage builds."
+ FORCE )
+set(CMAKE_C_FLAGS_COVERAGE
+ ${COVERAGE_COMPILER_FLAGS}
+ CACHE STRING "Flags used by the C compiler during coverage builds."
+ FORCE )
+set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
+ ""
+ CACHE STRING "Flags used for linking binaries during coverage builds."
+ FORCE )
+set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
+ ""
+ CACHE STRING "Flags used by the shared libraries linker during coverage builds."
+ FORCE )
+mark_as_advanced(
+ CMAKE_Fortran_FLAGS_COVERAGE
+ CMAKE_CXX_FLAGS_COVERAGE
+ CMAKE_C_FLAGS_COVERAGE
+ CMAKE_EXE_LINKER_FLAGS_COVERAGE
+ CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
+
+get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG))
+ message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
+endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)
+
+if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
+ link_libraries(gcov)
+endif()
+
+# Defines a target for running and collection code coverage information
+# Builds dependencies, runs the given executable and outputs reports.
+# NOTE! The executable should always have a ZERO as exit code otherwise
+# the coverage generation will not complete.
+#
+# setup_target_for_coverage_lcov(
+# NAME testrunner_coverage # New target name
+# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
+# DEPENDENCIES testrunner # Dependencies to build first
+# BASE_DIRECTORY "../" # Base directory for report
+# # (defaults to PROJECT_SOURCE_DIR)
+# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
+# # to BASE_DIRECTORY, with CMake 3.4+)
+# NO_DEMANGLE # Don't demangle C++ symbols
+# # even if c++filt is found
+# )
+function(setup_target_for_coverage_lcov)
+
+ set(options NO_DEMANGLE)
+ set(oneValueArgs BASE_DIRECTORY NAME)
+ set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS)
+ cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if(NOT LCOV_PATH)
+ message(FATAL_ERROR "lcov not found! Aborting...")
+ endif() # NOT LCOV_PATH
+
+ if(NOT GENHTML_PATH)
+ message(FATAL_ERROR "genhtml not found! Aborting...")
+ endif() # NOT GENHTML_PATH
+
+ # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
+ if(DEFINED Coverage_BASE_DIRECTORY)
+ get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
+ else()
+ set(BASEDIR ${PROJECT_SOURCE_DIR})
+ endif()
+
+ # Collect excludes (CMake 3.4+: Also compute absolute paths)
+ set(LCOV_EXCLUDES "")
+ foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES})
+ if(CMAKE_VERSION VERSION_GREATER 3.4)
+ get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
+ endif()
+ list(APPEND LCOV_EXCLUDES "${EXCLUDE}")
+ endforeach()
+ list(REMOVE_DUPLICATES LCOV_EXCLUDES)
+
+ # Conditional arguments
+ if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
+ set(GENHTML_EXTRA_ARGS "--demangle-cpp")
+ endif()
+
+ # Setting up commands which will be run to generate coverage data.
+ # Cleanup lcov
+ set(LCOV_CLEAN_CMD
+ ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory .
+ -b ${BASEDIR} --zerocounters
+ )
+ # Create baseline to make sure untouched files show up in the report
+ set(LCOV_BASELINE_CMD
+ ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b
+ ${BASEDIR} -o ${Coverage_NAME}.base
+ )
+ # Run tests
+ set(LCOV_EXEC_TESTS_CMD
+ ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
+ )
+ # Capturing lcov counters and generating report
+ set(LCOV_CAPTURE_CMD
+ ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b
+ ${BASEDIR} --capture --output-file ${Coverage_NAME}.capture
+ )
+ # add baseline counters
+ set(LCOV_BASELINE_COUNT_CMD
+ ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base
+ -a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total
+ )
+ # filter collected data to final coverage report
+ set(LCOV_FILTER_CMD
+ ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove
+ ${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info
+ )
+ # Generate HTML output
+ set(LCOV_GEN_HTML_CMD
+ ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o
+ ${Coverage_NAME} ${Coverage_NAME}.info
+ )
+
+
+ if(CODE_COVERAGE_VERBOSE)
+ message(STATUS "Executed command report")
+ message(STATUS "Command to clean up lcov: ")
+ string(REPLACE ";" " " LCOV_CLEAN_CMD_SPACED "${LCOV_CLEAN_CMD}")
+ message(STATUS "${LCOV_CLEAN_CMD_SPACED}")
+
+ message(STATUS "Command to create baseline: ")
+ string(REPLACE ";" " " LCOV_BASELINE_CMD_SPACED "${LCOV_BASELINE_CMD}")
+ message(STATUS "${LCOV_BASELINE_CMD_SPACED}")
+
+ message(STATUS "Command to run the tests: ")
+ string(REPLACE ";" " " LCOV_EXEC_TESTS_CMD_SPACED "${LCOV_EXEC_TESTS_CMD}")
+ message(STATUS "${LCOV_EXEC_TESTS_CMD_SPACED}")
+
+ message(STATUS "Command to capture counters and generate report: ")
+ string(REPLACE ";" " " LCOV_CAPTURE_CMD_SPACED "${LCOV_CAPTURE_CMD}")
+ message(STATUS "${LCOV_CAPTURE_CMD_SPACED}")
+
+ message(STATUS "Command to add baseline counters: ")
+ string(REPLACE ";" " " LCOV_BASELINE_COUNT_CMD_SPACED "${LCOV_BASELINE_COUNT_CMD}")
+ message(STATUS "${LCOV_BASELINE_COUNT_CMD_SPACED}")
+
+ message(STATUS "Command to filter collected data: ")
+ string(REPLACE ";" " " LCOV_FILTER_CMD_SPACED "${LCOV_FILTER_CMD}")
+ message(STATUS "${LCOV_FILTER_CMD_SPACED}")
+
+ message(STATUS "Command to generate lcov HTML output: ")
+ string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}")
+ message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}")
+ endif()
+
+ # Setup target
+ add_custom_target(${Coverage_NAME}
+ COMMAND ${LCOV_CLEAN_CMD}
+ COMMAND ${LCOV_BASELINE_CMD}
+ COMMAND ${LCOV_EXEC_TESTS_CMD}
+ COMMAND ${LCOV_CAPTURE_CMD}
+ COMMAND ${LCOV_BASELINE_COUNT_CMD}
+ COMMAND ${LCOV_FILTER_CMD}
+ COMMAND ${LCOV_GEN_HTML_CMD}
+
+ # Set output files as GENERATED (will be removed on 'make clean')
+ BYPRODUCTS
+ ${Coverage_NAME}.base
+ ${Coverage_NAME}.capture
+ ${Coverage_NAME}.total
+ ${Coverage_NAME}.info
+ ${Coverage_NAME}/index.html
+ WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+ DEPENDS ${Coverage_DEPENDENCIES}
+ VERBATIM # Protect arguments to commands
+ COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
+ )
+
+ # Show where to find the lcov info report
+ add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
+ COMMAND ;
+ COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info."
+ )
+
+ # Show info where to find the report
+ add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
+ COMMAND ;
+ COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
+ )
+
+endfunction() # setup_target_for_coverage_lcov
+
+# Defines a target for running and collection code coverage information
+# Builds dependencies, runs the given executable and outputs reports.
+# NOTE! The executable should always have a ZERO as exit code otherwise
+# the coverage generation will not complete.
+#
+# setup_target_for_coverage_gcovr_xml(
+# NAME ctest_coverage # New target name
+# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
+# DEPENDENCIES executable_target # Dependencies to build first
+# BASE_DIRECTORY "../" # Base directory for report
+# # (defaults to PROJECT_SOURCE_DIR)
+# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
+# # to BASE_DIRECTORY, with CMake 3.4+)
+# )
+# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
+# GCVOR command.
+function(setup_target_for_coverage_gcovr_xml)
+
+ set(options NONE)
+ set(oneValueArgs BASE_DIRECTORY NAME)
+ set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
+ cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if(NOT GCOVR_PATH)
+ message(FATAL_ERROR "gcovr not found! Aborting...")
+ endif() # NOT GCOVR_PATH
+
+ # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
+ if(DEFINED Coverage_BASE_DIRECTORY)
+ get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
+ else()
+ set(BASEDIR ${PROJECT_SOURCE_DIR})
+ endif()
+
+ # Collect excludes (CMake 3.4+: Also compute absolute paths)
+ set(GCOVR_EXCLUDES "")
+ foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
+ if(CMAKE_VERSION VERSION_GREATER 3.4)
+ get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
+ endif()
+ list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
+ endforeach()
+ list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
+
+ # Combine excludes to several -e arguments
+ set(GCOVR_EXCLUDE_ARGS "")
+ foreach(EXCLUDE ${GCOVR_EXCLUDES})
+ list(APPEND GCOVR_EXCLUDE_ARGS "-e")
+ list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
+ endforeach()
+
+ # Set up commands which will be run to generate coverage data
+ # Run tests
+ set(GCOVR_XML_EXEC_TESTS_CMD
+ ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
+ )
+ # Running gcovr
+ set(GCOVR_XML_CMD
+ ${GCOVR_PATH} --xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} ${GCOVR_EXCLUDE_ARGS}
+ --object-directory=${PROJECT_BINARY_DIR} -o ${Coverage_NAME}.xml
+ )
+
+ if(CODE_COVERAGE_VERBOSE)
+ message(STATUS "Executed command report")
+
+ message(STATUS "Command to run tests: ")
+ string(REPLACE ";" " " GCOVR_XML_EXEC_TESTS_CMD_SPACED "${GCOVR_XML_EXEC_TESTS_CMD}")
+ message(STATUS "${GCOVR_XML_EXEC_TESTS_CMD_SPACED}")
+
+ message(STATUS "Command to generate gcovr XML coverage data: ")
+ string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}")
+ message(STATUS "${GCOVR_XML_CMD_SPACED}")
+ endif()
+
+ add_custom_target(${Coverage_NAME}
+ COMMAND ${GCOVR_XML_EXEC_TESTS_CMD}
+ COMMAND ${GCOVR_XML_CMD}
+
+ BYPRODUCTS ${Coverage_NAME}.xml
+ WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+ DEPENDS ${Coverage_DEPENDENCIES}
+ VERBATIM # Protect arguments to commands
+ COMMENT "Running gcovr to produce Cobertura code coverage report."
+ )
+
+ # Show info where to find the report
+ add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
+ COMMAND ;
+ COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml."
+ )
+endfunction() # setup_target_for_coverage_gcovr_xml
+
+# Defines a target for running and collection code coverage information
+# Builds dependencies, runs the given executable and outputs reports.
+# NOTE! The executable should always have a ZERO as exit code otherwise
+# the coverage generation will not complete.
+#
+# setup_target_for_coverage_gcovr_html(
+# NAME ctest_coverage # New target name
+# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
+# DEPENDENCIES executable_target # Dependencies to build first
+# BASE_DIRECTORY "../" # Base directory for report
+# # (defaults to PROJECT_SOURCE_DIR)
+# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
+# # to BASE_DIRECTORY, with CMake 3.4+)
+# )
+# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
+# GCVOR command.
+function(setup_target_for_coverage_gcovr_html)
+
+ set(options NONE)
+ set(oneValueArgs BASE_DIRECTORY NAME)
+ set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
+ cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if(NOT GCOVR_PATH)
+ message(FATAL_ERROR "gcovr not found! Aborting...")
+ endif() # NOT GCOVR_PATH
+
+ # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
+ if(DEFINED Coverage_BASE_DIRECTORY)
+ get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
+ else()
+ set(BASEDIR ${PROJECT_SOURCE_DIR})
+ endif()
+
+ # Collect excludes (CMake 3.4+: Also compute absolute paths)
+ set(GCOVR_EXCLUDES "")
+ foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
+ if(CMAKE_VERSION VERSION_GREATER 3.4)
+ get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
+ endif()
+ list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
+ endforeach()
+ list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
+
+ # Combine excludes to several -e arguments
+ set(GCOVR_EXCLUDE_ARGS "")
+ foreach(EXCLUDE ${GCOVR_EXCLUDES})
+ list(APPEND GCOVR_EXCLUDE_ARGS "-e")
+ list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
+ endforeach()
+
+ # Set up commands which will be run to generate coverage data
+ # Run tests
+ set(GCOVR_HTML_EXEC_TESTS_CMD
+ ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
+ )
+ # Create folder
+ set(GCOVR_HTML_FOLDER_CMD
+ ${CMAKE_COMMAND} -E make_directory ${EXECUTABLE_OUTPUT_PATH}/${Coverage_NAME}
+ )
+ # Running gcovr
+ set(GCOVR_HTML_CMD
+ ${GCOVR_PATH} --html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
+ ${GCOVR_EXCLUDE_ARGS} --object-directory=${EXECUTABLE_OUTPUT_PATH}
+ -o ${Coverage_NAME}/index.html
+ )
+
+ if(CODE_COVERAGE_VERBOSE)
+ message(STATUS "Executed command report")
+
+ message(STATUS "Command to run tests: ")
+ string(REPLACE ";" " " GCOVR_HTML_EXEC_TESTS_CMD_SPACED "${GCOVR_HTML_EXEC_TESTS_CMD}")
+ message(STATUS "${GCOVR_HTML_EXEC_TESTS_CMD_SPACED}")
+
+ message(STATUS "Command to create a folder: ")
+ string(REPLACE ";" " " GCOVR_HTML_FOLDER_CMD_SPACED "${GCOVR_HTML_FOLDER_CMD}")
+ message(STATUS "${GCOVR_HTML_FOLDER_CMD_SPACED}")
+
+ message(STATUS "Command to generate gcovr HTML coverage data: ")
+ string(REPLACE ";" " " GCOVR_HTML_CMD_SPACED "${GCOVR_HTML_CMD}")
+ message(STATUS "${GCOVR_HTML_CMD_SPACED}")
+ endif()
+
+ add_custom_target(${Coverage_NAME} ALL
+ COMMAND ${GCOVR_HTML_EXEC_TESTS_CMD}
+ COMMAND ${GCOVR_HTML_FOLDER_CMD}
+ COMMAND ${GCOVR_HTML_CMD}
+
+ BYPRODUCTS ${EXECUTABLE_OUTPUT_PATH}/${Coverage_NAME}/index.html # report directory
+ WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}
+ DEPENDS ${Coverage_DEPENDENCIES}
+ VERBATIM # Protect arguments to commands
+ COMMENT "Running gcovr to produce HTML code coverage report."
+ )
+
+ # Show info where to find the report
+ add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
+ COMMAND ;
+ COMMENT "Open ${EXECUTABLE_OUTPUT_PATH}/${Coverage_NAME}/index.html in your browser to view the coverage report."
+ )
+
+endfunction() # setup_target_for_coverage_gcovr_html
+
+# Defines a target for running and collection code coverage information
+# Builds dependencies, runs the given executable and outputs reports.
+# NOTE! The executable should always have a ZERO as exit code otherwise
+# the coverage generation will not complete.
+#
+# setup_target_for_coverage_fastcov(
+# NAME testrunner_coverage # New target name
+# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
+# DEPENDENCIES testrunner # Dependencies to build first
+# BASE_DIRECTORY "../" # Base directory for report
+# # (defaults to PROJECT_SOURCE_DIR)
+# EXCLUDE "src/dir1/" "src/dir2/" # Patterns to exclude.
+# NO_DEMANGLE # Don't demangle C++ symbols
+# # even if c++filt is found
+# SKIP_HTML # Don't create html report
+# )
+function(setup_target_for_coverage_fastcov)
+
+ set(options NO_DEMANGLE SKIP_HTML)
+ set(oneValueArgs BASE_DIRECTORY NAME)
+ set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES FASTCOV_ARGS GENHTML_ARGS)
+ cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if(NOT FASTCOV_PATH)
+ message(FATAL_ERROR "fastcov not found! Aborting...")
+ endif()
+
+ if(NOT GENHTML_PATH)
+ message(FATAL_ERROR "genhtml not found! Aborting...")
+ endif()
+
+ # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
+ if(Coverage_BASE_DIRECTORY)
+ get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
+ else()
+ set(BASEDIR ${PROJECT_SOURCE_DIR})
+ endif()
+
+ # Collect excludes (Patterns, not paths, for fastcov)
+ set(FASTCOV_EXCLUDES "")
+ foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_FASTCOV_EXCLUDES})
+ list(APPEND FASTCOV_EXCLUDES "${EXCLUDE}")
+ endforeach()
+ list(REMOVE_DUPLICATES FASTCOV_EXCLUDES)
+
+ # Conditional arguments
+ if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
+ set(GENHTML_EXTRA_ARGS "--demangle-cpp")
+ endif()
+
+ # Set up commands which will be run to generate coverage data
+ set(FASTCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS})
+
+ set(FASTCOV_CAPTURE_CMD ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
+ --search-directory ${BASEDIR}
+ --process-gcno
+ --lcov
+ --output ${Coverage_NAME}.info
+ --exclude ${FASTCOV_EXCLUDES}
+ --exclude ${FASTCOV_EXCLUDES}
+ )
+
+ if(Coverage_SKIP_HTML)
+ set(FASTCOV_HTML_CMD ";")
+ else()
+ set(FASTCOV_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS}
+ -o ${Coverage_NAME} ${Coverage_NAME}.info
+ )
+ endif()
+
+ if(CODE_COVERAGE_VERBOSE)
+ message(STATUS "Code coverage commands for target ${Coverage_NAME} (fastcov):")
+
+ message(" Running tests:")
+ string(REPLACE ";" " " FASTCOV_EXEC_TESTS_CMD_SPACED "${FASTCOV_EXEC_TESTS_CMD}")
+ message(" ${FASTCOV_EXEC_TESTS_CMD_SPACED}")
+
+ message(" Capturing fastcov counters and generating report:")
+ string(REPLACE ";" " " FASTCOV_CAPTURE_CMD_SPACED "${FASTCOV_CAPTURE_CMD}")
+ message(" ${FASTCOV_CAPTURE_CMD_SPACED}")
+
+ if(NOT Coverage_SKIP_HTML)
+ message(" Generating HTML report: ")
+ string(REPLACE ";" " " FASTCOV_HTML_CMD_SPACED "${FASTCOV_HTML_CMD}")
+ message(" ${FASTCOV_HTML_CMD_SPACED}")
+ endif()
+ endif()
+
+ # Setup target
+ add_custom_target(${Coverage_NAME}
+
+ # Cleanup fastcov
+ COMMAND ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
+ --search-directory ${BASEDIR}
+ --zerocounters
+
+ COMMAND ${FASTCOV_EXEC_TESTS_CMD}
+ COMMAND ${FASTCOV_CAPTURE_CMD}
+ COMMAND ${FASTCOV_HTML_CMD}
+
+ # Set output files as GENERATED (will be removed on 'make clean')
+ BYPRODUCTS
+ ${Coverage_NAME}.info
+ ${Coverage_NAME}/index.html # report directory
+
+ WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+ DEPENDS ${Coverage_DEPENDENCIES}
+ VERBATIM # Protect arguments to commands
+ COMMENT "Resetting code coverage counters to zero. Processing code coverage counters and generating report."
+ )
+
+ set(INFO_MSG "fastcov code coverage info report saved in ${Coverage_NAME}.info.")
+ if(NOT Coverage_SKIP_HTML)
+ string(APPEND INFO_MSG " Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report.")
+ endif()
+ # Show where to find the fastcov info report
+ add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E echo ${INFO_MSG}
+ )
+
+endfunction() # setup_target_for_coverage_fastcov
+
+function(append_coverage_compiler_flags)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
+ set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
+ message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
+endfunction() # append_coverage_compiler_flags
\ No newline at end of file
diff --git a/components/connectivity/iot-hub-device-c-sdk/config/mosquitto/mosquitto.conf b/components/connectivity/iot-hub-device-c-sdk/config/mosquitto/mosquitto.conf
new file mode 100644
index 00000000..fa4cbd4d
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/config/mosquitto/mosquitto.conf
@@ -0,0 +1,874 @@
+# Config file for mosquitto
+#
+# See mosquitto.conf(5) for more information.
+#
+# Default values are shown, uncomment to change.
+#
+# Use the # character to indicate a comment, but only if it is the
+# very first character on the line.
+
+# =================================================================
+# General configuration
+# =================================================================
+
+# Use per listener security settings.
+#
+# It is recommended this option be set before any other options.
+#
+# If this option is set to true, then all authentication and access control
+# options are controlled on a per listener basis. The following options are
+# affected:
+#
+# password_file acl_file psk_file auth_plugin auth_opt_* allow_anonymous
+# auto_id_prefix allow_zero_length_clientid
+#
+# Note that if set to true, then a durable client (i.e. with clean session set
+# to false) that has disconnected will use the ACL settings defined for the
+# listener that it was most recently connected to.
+#
+# The default behaviour is for this to be set to false, which maintains the
+# setting behaviour from previous versions of mosquitto.
+#per_listener_settings false
+
+
+# This option controls whether a client is allowed to connect with a zero
+# length client id or not. This option only affects clients using MQTT v3.1.1
+# and later. If set to false, clients connecting with a zero length client id
+# are disconnected. If set to true, clients will be allocated a client id by
+# the broker. This means it is only useful for clients with clean session set
+# to true.
+#allow_zero_length_clientid true
+
+# If allow_zero_length_clientid is true, this option allows you to set a prefix
+# to automatically generated client ids to aid visibility in logs.
+# Defaults to 'auto-'
+#auto_id_prefix auto-
+
+# This option affects the scenario when a client subscribes to a topic that has
+# retained messages. It is possible that the client that published the retained
+# message to the topic had access at the time they published, but that access
+# has been subsequently removed. If check_retain_source is set to true, the
+# default, the source of a retained message will be checked for access rights
+# before it is republished. When set to false, no check will be made and the
+# retained message will always be published. This affects all listeners.
+#check_retain_source true
+
+# QoS 1 and 2 messages will be allowed inflight per client until this limit
+# is exceeded. Defaults to 0. (No maximum)
+# See also max_inflight_messages
+#max_inflight_bytes 0
+
+# The maximum number of QoS 1 and 2 messages currently inflight per
+# client.
+# This includes messages that are partway through handshakes and
+# those that are being retried. Defaults to 20. Set to 0 for no
+# maximum. Setting to 1 will guarantee in-order delivery of QoS 1
+# and 2 messages.
+#max_inflight_messages 20
+
+# For MQTT v5 clients, it is possible to have the server send a "server
+# keepalive" value that will override the keepalive value set by the client.
+# This is intended to be used as a mechanism to say that the server will
+# disconnect the client earlier than it anticipated, and that the client should
+# use the new keepalive value. The max_keepalive option allows you to specify
+# that clients may only connect with keepalive less than or equal to this
+# value, otherwise they will be sent a server keepalive telling them to use
+# max_keepalive. This only applies to MQTT v5 clients. The maximum value
+# allowable is 65535. Do not set below 10.
+#max_keepalive 65535
+
+# For MQTT v5 clients, it is possible to have the server send a "maximum packet
+# size" value that will instruct the client it will not accept MQTT packets
+# with size greater than max_packet_size bytes. This applies to the full MQTT
+# packet, not just the payload. Setting this option to a positive value will
+# set the maximum packet size to that number of bytes. If a client sends a
+# packet which is larger than this value, it will be disconnected. This applies
+# to all clients regardless of the protocol version they are using, but v3.1.1
+# and earlier clients will of course not have received the maximum packet size
+# information. Defaults to no limit. Setting below 20 bytes is forbidden
+# because it is likely to interfere with ordinary client operation, even with
+# very small payloads.
+#max_packet_size 0
+
+# QoS 1 and 2 messages above those currently in-flight will be queued per
+# client until this limit is exceeded. Defaults to 0. (No maximum)
+# See also max_queued_messages.
+# If both max_queued_messages and max_queued_bytes are specified, packets will
+# be queued until the first limit is reached.
+#max_queued_bytes 0
+
+# Set the maximum QoS supported. Clients publishing at a QoS higher than
+# specified here will be disconnected.
+#max_qos 2
+
+# The maximum number of QoS 1 and 2 messages to hold in a queue per client
+# above those that are currently in-flight. Defaults to 1000. Set
+# to 0 for no maximum (not recommended).
+# See also queue_qos0_messages.
+# See also max_queued_bytes.
+#max_queued_messages 1000
+#
+# This option sets the maximum number of heap memory bytes that the broker will
+# allocate, and hence sets a hard limit on memory use by the broker. Memory
+# requests that exceed this value will be denied. The effect will vary
+# depending on what has been denied. If an incoming message is being processed,
+# then the message will be dropped and the publishing client will be
+# disconnected. If an outgoing message is being sent, then the individual
+# message will be dropped and the receiving client will be disconnected.
+# Defaults to no limit.
+#memory_limit 0
+
+# This option sets the maximum publish payload size that the broker will allow.
+# Received messages that exceed this size will not be accepted by the broker.
+# The default value is 0, which means that all valid MQTT messages are
+# accepted. MQTT imposes a maximum payload size of 268435455 bytes.
+#message_size_limit 0
+
+# This option allows persistent clients (those with clean session set to false)
+# to be removed if they do not reconnect within a certain time frame.
+#
+# This is a non-standard option in MQTT V3.1 but allowed in MQTT v3.1.1.
+#
+# Badly designed clients may set clean session to false whilst using a randomly
+# generated client id. This leads to persistent clients that will never
+# reconnect. This option allows these clients to be removed.
+#
+# The expiration period should be an integer followed by one of h d w m y for
+# hour, day, week, month and year respectively. For example
+#
+# persistent_client_expiration 2m
+# persistent_client_expiration 14d
+# persistent_client_expiration 1y
+#
+# The default if not set is to never expire persistent clients.
+#persistent_client_expiration
+
+# Write process id to a file. Default is a blank string which means
+# a pid file shouldn't be written.
+# This should be set to /var/run/mosquitto/mosquitto.pid if mosquitto is
+# being run automatically on boot with an init script and
+# start-stop-daemon or similar.
+#pid_file
+
+# Set to true to queue messages with QoS 0 when a persistent client is
+# disconnected. These messages are included in the limit imposed by
+# max_queued_messages and max_queued_bytes
+# Defaults to false.
+# This is a non-standard option for the MQTT v3.1 spec but is allowed in
+# v3.1.1.
+#queue_qos0_messages false
+
+# Set to false to disable retained message support. If a client publishes a
+# message with the retain bit set, it will be disconnected if this is set to
+# false.
+#retain_available true
+
+# Disable Nagle's algorithm on client sockets. This has the effect of reducing
+# latency of individual messages at the potential cost of increasing the number
+# of packets being sent.
+#set_tcp_nodelay false
+
+# Time in seconds between updates of the $SYS tree.
+# Set to 0 to disable the publishing of the $SYS tree.
+#sys_interval 10
+
+# The MQTT specification requires that the QoS of a message delivered to a
+# subscriber is never upgraded to match the QoS of the subscription. Enabling
+# this option changes this behaviour. If upgrade_outgoing_qos is set true,
+# messages sent to a subscriber will always match the QoS of its subscription.
+# This is a non-standard option explicitly disallowed by the spec.
+#upgrade_outgoing_qos false
+
+# When run as root, drop privileges to this user and its primary
+# group.
+# Set to root to stay as root, but this is not recommended.
+# If set to "mosquitto", or left unset, and the "mosquitto" user does not exist
+# then it will drop privileges to the "nobody" user instead.
+# If run as a non-root user, this setting has no effect.
+# Note that on Windows this has no effect and so mosquitto should be started by
+# the user you wish it to run as.
+#user mosquitto
+
+# =================================================================
+# Listeners
+# =================================================================
+
+# Listen on a port/ip address combination. By using this variable
+# multiple times, mosquitto can listen on more than one port. If
+# this variable is used and neither bind_address nor port given,
+# then the default listener will not be started.
+# The port number to listen on must be given. Optionally, an ip
+# address or host name may be supplied as a second argument. In
+# this case, mosquitto will attempt to bind the listener to that
+# address and so restrict access to the associated network and
+# interface. By default, mosquitto will listen on all interfaces.
+# Note that for a websockets listener it is not possible to bind to a host
+# name.
+#
+# On systems that support Unix Domain Sockets, it is also possible
+# to create a # Unix socket rather than opening a TCP socket. In
+# this case, the port number should be set to 0 and a unix socket
+# path must be provided, e.g.
+# listener 0 /tmp/mosquitto.sock
+#
+# listener port-number [ip address/host name/unix socket path]
+#listener
+
+# By default, a listener will attempt to listen on all supported IP protocol
+# versions. If you do not have an IPv4 or IPv6 interface you may wish to
+# disable support for either of those protocol versions. In particular, note
+# that due to the limitations of the websockets library, it will only ever
+# attempt to open IPv6 sockets if IPv6 support is compiled in, and so will fail
+# if IPv6 is not available.
+#
+# Set to `ipv4` to force the listener to only use IPv4, or set to `ipv6` to
+# force the listener to only use IPv6. If you want support for both IPv4 and
+# IPv6, then do not use the socket_domain option.
+#
+#socket_domain
+
+# Bind the listener to a specific interface. This is similar to
+# the [ip address/host name] part of the listener definition, but is useful
+# when an interface has multiple addresses or the address may change. If used
+# with the [ip address/host name] part of the listener definition, then the
+# bind_interface option will take priority.
+# Not available on Windows.
+#
+# Example: bind_interface eth0
+#bind_interface
+
+# When a listener is using the websockets protocol, it is possible to serve
+# http data as well. Set http_dir to a directory which contains the files you
+# wish to serve. If this option is not specified, then no normal http
+# connections will be possible.
+#http_dir
+
+# The maximum number of client connections to allow. This is
+# a per listener setting.
+# Default is -1, which means unlimited connections.
+# Note that other process limits mean that unlimited connections
+# are not really possible. Typically the default maximum number of
+# connections possible is around 1024.
+#max_connections -1
+
+# The listener can be restricted to operating within a topic hierarchy using
+# the mount_point option. This is achieved be prefixing the mount_point string
+# to all topics for any clients connected to this listener. This prefixing only
+# happens internally to the broker; the client will not see the prefix.
+#mount_point
+
+# Choose the protocol to use when listening.
+# This can be either mqtt or websockets.
+# Certificate based TLS may be used with websockets, except that only the
+# cafile, certfile, keyfile, ciphers, and ciphers_tls13 options are supported.
+#protocol mqtt
+
+# Set use_username_as_clientid to true to replace the clientid that a client
+# connected with with its username. This allows authentication to be tied to
+# the clientid, which means that it is possible to prevent one client
+# disconnecting another by using the same clientid.
+# If a client connects with no username it will be disconnected as not
+# authorised when this option is set to true.
+# Do not use in conjunction with clientid_prefixes.
+# See also use_identity_as_username.
+#use_username_as_clientid
+
+# Change the websockets headers size. This is a global option, it is not
+# possible to set per listener. This option sets the size of the buffer used in
+# the libwebsockets library when reading HTTP headers. If you are passing large
+# header data such as cookies then you may need to increase this value. If left
+# unset, or set to 0, then the default of 1024 bytes will be used.
+#websockets_headers_size
+
+# -----------------------------------------------------------------
+# Certificate based SSL/TLS support
+# -----------------------------------------------------------------
+# The following options can be used to enable certificate based SSL/TLS support
+# for this listener. Note that the recommended port for MQTT over TLS is 8883,
+# but this must be set manually.
+#
+# See also the mosquitto-tls man page and the "Pre-shared-key based SSL/TLS
+# support" section. Only one of certificate or PSK encryption support can be
+# enabled for any listener.
+
+# Both of certfile and keyfile must be defined to enable certificate based
+# TLS encryption.
+
+# Path to the PEM encoded server certificate.
+#certfile
+
+# Path to the PEM encoded keyfile.
+#keyfile
+
+# If you wish to control which encryption ciphers are used, use the ciphers
+# option. The list of available ciphers can be optained using the "openssl
+# ciphers" command and should be provided in the same format as the output of
+# that command. This applies to TLS 1.2 and earlier versions only. Use
+# ciphers_tls1.3 for TLS v1.3.
+#ciphers
+
+# Choose which TLS v1.3 ciphersuites are used for this listener.
+# Defaults to "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256"
+#ciphers_tls1.3
+
+# If you have require_certificate set to true, you can create a certificate
+# revocation list file to revoke access to particular client certificates. If
+# you have done this, use crlfile to point to the PEM encoded revocation file.
+#crlfile
+
+# To allow the use of ephemeral DH key exchange, which provides forward
+# security, the listener must load DH parameters. This can be specified with
+# the dhparamfile option. The dhparamfile can be generated with the command
+# e.g. "openssl dhparam -out dhparam.pem 2048"
+#dhparamfile
+
+# By default an TLS enabled listener will operate in a similar fashion to a
+# https enabled web server, in that the server has a certificate signed by a CA
+# and the client will verify that it is a trusted certificate. The overall aim
+# is encryption of the network traffic. By setting require_certificate to true,
+# the client must provide a valid certificate in order for the network
+# connection to proceed. This allows access to the broker to be controlled
+# outside of the mechanisms provided by MQTT.
+#require_certificate false
+
+# cafile and capath define methods of accessing the PEM encoded
+# Certificate Authority certificates that will be considered trusted when
+# checking incoming client certificates.
+# cafile defines the path to a file containing the CA certificates.
+# capath defines a directory that will be searched for files
+# containing the CA certificates. For capath to work correctly, the
+# certificate files must have ".crt" as the file ending and you must run
+# "openssl rehash " each time you add/remove a certificate.
+#cafile
+#capath
+
+
+# If require_certificate is true, you may set use_identity_as_username to true
+# to use the CN value from the client certificate as a username. If this is
+# true, the password_file option will not be used for this listener.
+#use_identity_as_username false
+
+# -----------------------------------------------------------------
+# Pre-shared-key based SSL/TLS support
+# -----------------------------------------------------------------
+# The following options can be used to enable PSK based SSL/TLS support for
+# this listener. Note that the recommended port for MQTT over TLS is 8883, but
+# this must be set manually.
+#
+# See also the mosquitto-tls man page and the "Certificate based SSL/TLS
+# support" section. Only one of certificate or PSK encryption support can be
+# enabled for any listener.
+
+# The psk_hint option enables pre-shared-key support for this listener and also
+# acts as an identifier for this listener. The hint is sent to clients and may
+# be used locally to aid authentication. The hint is a free form string that
+# doesn't have much meaning in itself, so feel free to be creative.
+# If this option is provided, see psk_file to define the pre-shared keys to be
+# used or create a security plugin to handle them.
+#psk_hint
+
+# When using PSK, the encryption ciphers used will be chosen from the list of
+# available PSK ciphers. If you want to control which ciphers are available,
+# use the "ciphers" option. The list of available ciphers can be optained
+# using the "openssl ciphers" command and should be provided in the same format
+# as the output of that command.
+#ciphers
+
+# Set use_identity_as_username to have the psk identity sent by the client used
+# as its username. Authentication will be carried out using the PSK rather than
+# the MQTT username/password and so password_file will not be used for this
+# listener.
+#use_identity_as_username false
+
+
+# =================================================================
+# Persistence
+# =================================================================
+
+# If persistence is enabled, save the in-memory database to disk
+# every autosave_interval seconds. If set to 0, the persistence
+# database will only be written when mosquitto exits. See also
+# autosave_on_changes.
+# Note that writing of the persistence database can be forced by
+# sending mosquitto a SIGUSR1 signal.
+#autosave_interval 1800
+
+# If true, mosquitto will count the number of subscription changes, retained
+# messages received and queued messages and if the total exceeds
+# autosave_interval then the in-memory database will be saved to disk.
+# If false, mosquitto will save the in-memory database to disk by treating
+# autosave_interval as a time in seconds.
+#autosave_on_changes false
+
+# Save persistent message data to disk (true/false).
+# This saves information about all messages, including
+# subscriptions, currently in-flight messages and retained
+# messages.
+# retained_persistence is a synonym for this option.
+#persistence false
+
+# The filename to use for the persistent database, not including
+# the path.
+#persistence_file mosquitto.db
+
+# Location for persistent database.
+# Default is an empty string (current directory).
+# Set to e.g. /var/lib/mosquitto if running as a proper service on Linux or
+# similar.
+#persistence_location
+
+
+# =================================================================
+# Logging
+# =================================================================
+
+# Places to log to. Use multiple log_dest lines for multiple
+# logging destinations.
+# Possible destinations are: stdout stderr syslog topic file dlt
+#
+# stdout and stderr log to the console on the named output.
+#
+# syslog uses the userspace syslog facility which usually ends up
+# in /var/log/messages or similar.
+#
+# topic logs to the broker topic '$SYS/broker/log/',
+# where severity is one of D, E, W, N, I, M which are debug, error,
+# warning, notice, information and message. Message type severity is used by
+# the subscribe/unsubscribe log_types and publishes log messages to
+# $SYS/broker/log/M/susbcribe or $SYS/broker/log/M/unsubscribe.
+#
+# The file destination requires an additional parameter which is the file to be
+# logged to, e.g. "log_dest file /var/log/mosquitto.log". The file will be
+# closed and reopened when the broker receives a HUP signal. Only a single file
+# destination may be configured.
+#
+# The dlt destination is for the automotive `Diagnostic Log and Trace` tool.
+# This requires that Mosquitto has been compiled with DLT support.
+#
+# Note that if the broker is running as a Windows service it will default to
+# "log_dest none" and neither stdout nor stderr logging is available.
+# Use "log_dest none" if you wish to disable logging.
+#log_dest stderr
+
+# Types of messages to log. Use multiple log_type lines for logging
+# multiple types of messages.
+# Possible types are: debug, error, warning, notice, information,
+# none, subscribe, unsubscribe, websockets, all.
+# Note that debug type messages are for decoding the incoming/outgoing
+# network packets. They are not logged in "topics".
+#log_type error
+#log_type warning
+#log_type notice
+#log_type information
+log_type all
+
+# If set to true, client connection and disconnection messages will be included
+# in the log.
+connection_messages true
+
+# If using syslog logging (not on Windows), messages will be logged to the
+# "daemon" facility by default. Use the log_facility option to choose which of
+# local0 to local7 to log to instead. The option value should be an integer
+# value, e.g. "log_facility 5" to use local5.
+#log_facility
+
+# If set to true, add a timestamp value to each log message.
+log_timestamp true
+
+# Set the format of the log timestamp. If left unset, this is the number of
+# seconds since the Unix epoch.
+# This is a free text string which will be passed to the strftime function. To
+# get an ISO 8601 datetime, for example:
+# log_timestamp_format %Y-%m-%dT%H:%M:%S
+#log_timestamp_format
+
+# Change the websockets logging level. This is a global option, it is not
+# possible to set per listener. This is an integer that is interpreted by
+# libwebsockets as a bit mask for its lws_log_levels enum. See the
+# libwebsockets documentation for more details. "log_type websockets" must also
+# be enabled.
+#websockets_log_level 0
+
+
+# =================================================================
+# Security
+# =================================================================
+
+# If set, only clients that have a matching prefix on their
+# clientid will be allowed to connect to the broker. By default,
+# all clients may connect.
+# For example, setting "secure-" here would mean a client "secure-
+# client" could connect but another with clientid "mqtt" couldn't.
+#clientid_prefixes
+
+# Boolean value that determines whether clients that connect
+# without providing a username are allowed to connect. If set to
+# false then a password file should be created (see the
+# password_file option) to control authenticated client access.
+#
+# Defaults to false, unless there are no listeners defined in the configuration
+# file, in which case it is set to true, but connections are only allowed from
+# the local machine.
+allow_anonymous true
+
+# -----------------------------------------------------------------
+# Default authentication and topic access control
+# -----------------------------------------------------------------
+
+# Control access to the broker using a password file. This file can be
+# generated using the mosquitto_passwd utility. If TLS support is not compiled
+# into mosquitto (it is recommended that TLS support should be included) then
+# plain text passwords are used, in which case the file should be a text file
+# with lines in the format:
+# username:password
+# The password (and colon) may be omitted if desired, although this
+# offers very little in the way of security.
+#
+# See the TLS client require_certificate and use_identity_as_username options
+# for alternative authentication options. If an auth_plugin is used as well as
+# password_file, the auth_plugin check will be made first.
+#password_file
+
+# Access may also be controlled using a pre-shared-key file. This requires
+# TLS-PSK support and a listener configured to use it. The file should be text
+# lines in the format:
+# identity:key
+# The key should be in hexadecimal format without a leading "0x".
+# If an auth_plugin is used as well, the auth_plugin check will be made first.
+#psk_file
+
+# Control access to topics on the broker using an access control list
+# file. If this parameter is defined then only the topics listed will
+# have access.
+# If the first character of a line of the ACL file is a # it is treated as a
+# comment.
+# Topic access is added with lines of the format:
+#
+# topic [read|write|readwrite|deny]
+#
+# The access type is controlled using "read", "write", "readwrite" or "deny".
+# This parameter is optional (unless contains a space character) - if
+# not given then the access is read/write. can contain the + or #
+# wildcards as in subscriptions.
+#
+# The "deny" option can used to explicity deny access to a topic that would
+# otherwise be granted by a broader read/write/readwrite statement. Any "deny"
+# topics are handled before topics that grant read/write access.
+#
+# The first set of topics are applied to anonymous clients, assuming
+# allow_anonymous is true. User specific topic ACLs are added after a
+# user line as follows:
+#
+# user
+#
+# The username referred to here is the same as in password_file. It is
+# not the clientid.
+#
+#
+# If is also possible to define ACLs based on pattern substitution within the
+# topic. The patterns available for substition are:
+#
+# %c to match the client id of the client
+# %u to match the username of the client
+#
+# The substitution pattern must be the only text for that level of hierarchy.
+#
+# The form is the same as for the topic keyword, but using pattern as the
+# keyword.
+# Pattern ACLs apply to all users even if the "user" keyword has previously
+# been given.
+#
+# If using bridges with usernames and ACLs, connection messages can be allowed
+# with the following pattern:
+# pattern write $SYS/broker/connection/%c/state
+#
+# pattern [read|write|readwrite]
+#
+# Example:
+#
+# pattern write sensor/%u/data
+#
+# If an auth_plugin is used as well as acl_file, the auth_plugin check will be
+# made first.
+#acl_file
+
+# -----------------------------------------------------------------
+# External authentication and topic access plugin options
+# -----------------------------------------------------------------
+
+# External authentication and access control can be supported with the
+# auth_plugin option. This is a path to a loadable plugin. See also the
+# auth_opt_* options described below.
+#
+# The auth_plugin option can be specified multiple times to load multiple
+# plugins. The plugins will be processed in the order that they are specified
+# here. If the auth_plugin option is specified alongside either of
+# password_file or acl_file then the plugin checks will be made first.
+#
+#auth_plugin
+
+# If the auth_plugin option above is used, define options to pass to the
+# plugin here as described by the plugin instructions. All options named
+# using the format auth_opt_* will be passed to the plugin, for example:
+#
+# auth_opt_db_host
+# auth_opt_db_port
+# auth_opt_db_username
+# auth_opt_db_password
+
+
+# =================================================================
+# Bridges
+# =================================================================
+
+# A bridge is a way of connecting multiple MQTT brokers together.
+# Create a new bridge using the "connection" option as described below. Set
+# options for the bridges using the remaining parameters. You must specify the
+# address and at least one topic to subscribe to.
+#
+# Each connection must have a unique name.
+#
+# The address line may have multiple host address and ports specified. See
+# below in the round_robin description for more details on bridge behaviour if
+# multiple addresses are used. Note that if you use an IPv6 address, then you
+# are required to specify a port.
+#
+# The direction that the topic will be shared can be chosen by
+# specifying out, in or both, where the default value is out.
+# The QoS level of the bridged communication can be specified with the next
+# topic option. The default QoS level is 0, to change the QoS the topic
+# direction must also be given.
+#
+# The local and remote prefix options allow a topic to be remapped when it is
+# bridged to/from the remote broker. This provides the ability to place a topic
+# tree in an appropriate location.
+#
+# For more details see the mosquitto.conf man page.
+#
+# Multiple topics can be specified per connection, but be careful
+# not to create any loops.
+#
+# If you are using bridges with cleansession set to false (the default), then
+# you may get unexpected behaviour from incoming topics if you change what
+# topics you are subscribing to. This is because the remote broker keeps the
+# subscription for the old topic. If you have this problem, connect your bridge
+# with cleansession set to true, then reconnect with cleansession set to false
+# as normal.
+#connection
+#address [:] [[:]]
+#topic [[[out | in | both] qos-level] local-prefix remote-prefix]
+
+# If you need to have the bridge connect over a particular network interface,
+# use bridge_bind_address to tell the bridge which local IP address the socket
+# should bind to, e.g. `bridge_bind_address 192.168.1.10`
+#bridge_bind_address
+
+# If a bridge has topics that have "out" direction, the default behaviour is to
+# send an unsubscribe request to the remote broker on that topic. This means
+# that changing a topic direction from "in" to "out" will not keep receiving
+# incoming messages. Sending these unsubscribe requests is not always
+# desirable, setting bridge_attempt_unsubscribe to false will disable sending
+# the unsubscribe request.
+#bridge_attempt_unsubscribe true
+
+# Set the version of the MQTT protocol to use with for this bridge. Can be one
+# of mqttv50, mqttv311 or mqttv31. Defaults to mqttv311.
+#bridge_protocol_version mqttv311
+
+# Set the clean session variable for this bridge.
+# When set to true, when the bridge disconnects for any reason, all
+# messages and subscriptions will be cleaned up on the remote
+# broker. Note that with cleansession set to true, there may be a
+# significant amount of retained messages sent when the bridge
+# reconnects after losing its connection.
+# When set to false, the subscriptions and messages are kept on the
+# remote broker, and delivered when the bridge reconnects.
+#cleansession false
+
+# Set the amount of time a bridge using the lazy start type must be idle before
+# it will be stopped. Defaults to 60 seconds.
+#idle_timeout 60
+
+# Set the keepalive interval for this bridge connection, in
+# seconds.
+#keepalive_interval 60
+
+# Set the clientid to use on the local broker. If not defined, this defaults to
+# 'local.'. If you are bridging a broker to itself, it is important
+# that local_clientid and clientid do not match.
+#local_clientid
+
+# If set to true, publish notification messages to the local and remote brokers
+# giving information about the state of the bridge connection. Retained
+# messages are published to the topic $SYS/broker/connection//state
+# unless the notification_topic option is used.
+# If the message is 1 then the connection is active, or 0 if the connection has
+# failed.
+# This uses the last will and testament feature.
+#notifications true
+
+# Choose the topic on which notification messages for this bridge are
+# published. If not set, messages are published on the topic
+# $SYS/broker/connection//state
+#notification_topic
+
+# Set the client id to use on the remote end of this bridge connection. If not
+# defined, this defaults to 'name.hostname' where name is the connection name
+# and hostname is the hostname of this computer.
+# This replaces the old "clientid" option to avoid confusion. "clientid"
+# remains valid for the time being.
+#remote_clientid
+
+# Set the password to use when connecting to a broker that requires
+# authentication. This option is only used if remote_username is also set.
+# This replaces the old "password" option to avoid confusion. "password"
+# remains valid for the time being.
+#remote_password
+
+# Set the username to use when connecting to a broker that requires
+# authentication.
+# This replaces the old "username" option to avoid confusion. "username"
+# remains valid for the time being.
+#remote_username
+
+# Set the amount of time a bridge using the automatic start type will wait
+# until attempting to reconnect.
+# This option can be configured to use a constant delay time in seconds, or to
+# use a backoff mechanism based on "Decorrelated Jitter", which adds a degree
+# of randomness to when the restart occurs.
+#
+# Set a constant timeout of 20 seconds:
+# restart_timeout 20
+#
+# Set backoff with a base (start value) of 10 seconds and a cap (upper limit) of
+# 60 seconds:
+# restart_timeout 10 30
+#
+# Defaults to jitter with a base of 5 and cap of 30
+#restart_timeout 5 30
+
+# If the bridge has more than one address given in the address/addresses
+# configuration, the round_robin option defines the behaviour of the bridge on
+# a failure of the bridge connection. If round_robin is false, the default
+# value, then the first address is treated as the main bridge connection. If
+# the connection fails, the other secondary addresses will be attempted in
+# turn. Whilst connected to a secondary bridge, the bridge will periodically
+# attempt to reconnect to the main bridge until successful.
+# If round_robin is true, then all addresses are treated as equals. If a
+# connection fails, the next address will be tried and if successful will
+# remain connected until it fails
+#round_robin false
+
+# Set the start type of the bridge. This controls how the bridge starts and
+# can be one of three types: automatic, lazy and once. Note that RSMB provides
+# a fourth start type "manual" which isn't currently supported by mosquitto.
+#
+# "automatic" is the default start type and means that the bridge connection
+# will be started automatically when the broker starts and also restarted
+# after a short delay (30 seconds) if the connection fails.
+#
+# Bridges using the "lazy" start type will be started automatically when the
+# number of queued messages exceeds the number set with the "threshold"
+# parameter. It will be stopped automatically after the time set by the
+# "idle_timeout" parameter. Use this start type if you wish the connection to
+# only be active when it is needed.
+#
+# A bridge using the "once" start type will be started automatically when the
+# broker starts but will not be restarted if the connection fails.
+#start_type automatic
+
+# Set the number of messages that need to be queued for a bridge with lazy
+# start type to be restarted. Defaults to 10 messages.
+# Must be less than max_queued_messages.
+#threshold 10
+
+# If try_private is set to true, the bridge will attempt to indicate to the
+# remote broker that it is a bridge not an ordinary client. If successful, this
+# means that loop detection will be more effective and that retained messages
+# will be propagated correctly. Not all brokers support this feature so it may
+# be necessary to set try_private to false if your bridge does not connect
+# properly.
+#try_private true
+
+# Some MQTT brokers do not allow retained messages. MQTT v5 gives a mechanism
+# for brokers to tell clients that they do not support retained messages, but
+# this is not possible for MQTT v3.1.1 or v3.1. If you need to bridge to a
+# v3.1.1 or v3.1 broker that does not support retained messages, set the
+# bridge_outgoing_retain option to false. This will remove the retain bit on
+# all outgoing messages to that bridge, regardless of any other setting.
+#bridge_outgoing_retain true
+
+# If you wish to restrict the size of messages sent to a remote bridge, use the
+# bridge_max_packet_size option. This sets the maximum number of bytes for
+# the total message, including headers and payload.
+# Note that MQTT v5 brokers may provide their own maximum-packet-size property.
+# In this case, the smaller of the two limits will be used.
+# Set to 0 for "unlimited".
+#bridge_max_packet_size 0
+
+
+# -----------------------------------------------------------------
+# Certificate based SSL/TLS support
+# -----------------------------------------------------------------
+# Either bridge_cafile or bridge_capath must be defined to enable TLS support
+# for this bridge.
+# bridge_cafile defines the path to a file containing the
+# Certificate Authority certificates that have signed the remote broker
+# certificate.
+# bridge_capath defines a directory that will be searched for files containing
+# the CA certificates. For bridge_capath to work correctly, the certificate
+# files must have ".crt" as the file ending and you must run "openssl rehash
+# " each time you add/remove a certificate.
+#bridge_cafile
+#bridge_capath
+
+
+# If the remote broker has more than one protocol available on its port, e.g.
+# MQTT and WebSockets, then use bridge_alpn to configure which protocol is
+# requested. Note that WebSockets support for bridges is not yet available.
+#bridge_alpn
+
+# When using certificate based encryption, bridge_insecure disables
+# verification of the server hostname in the server certificate. This can be
+# useful when testing initial server configurations, but makes it possible for
+# a malicious third party to impersonate your server through DNS spoofing, for
+# example. Use this option in testing only. If you need to resort to using this
+# option in a production environment, your setup is at fault and there is no
+# point using encryption.
+#bridge_insecure false
+
+# Path to the PEM encoded client certificate, if required by the remote broker.
+#bridge_certfile
+
+# Path to the PEM encoded client private key, if required by the remote broker.
+#bridge_keyfile
+
+# -----------------------------------------------------------------
+# PSK based SSL/TLS support
+# -----------------------------------------------------------------
+# Pre-shared-key encryption provides an alternative to certificate based
+# encryption. A bridge can be configured to use PSK with the bridge_identity
+# and bridge_psk options. These are the client PSK identity, and pre-shared-key
+# in hexadecimal format with no "0x". Only one of certificate and PSK based
+# encryption can be used on one
+# bridge at once.
+#bridge_identity
+#bridge_psk
+
+
+# =================================================================
+# External config files
+# =================================================================
+
+# External configuration files may be included by using the
+# include_dir option. This defines a directory that will be searched
+# for config files. All files that end in '.conf' will be loaded as
+# a configuration file. It is best to have this as the last option
+# in the main file. This option will only be processed from the main
+# configuration file. The directory specified must not contain the
+# main configuration file.
+# Files within include_dir will be loaded sorted in case-sensitive
+# alphabetical order, with capital letters ordered first. If this option is
+# given multiple times, all of the files from the first instance will be
+# processed before the next instance. See the man page for examples.
+#include_dir
diff --git a/components/connectivity/iot-hub-device-c-sdk/config/quectel/CMakeLists.txt b/components/connectivity/iot-hub-device-c-sdk/config/quectel/CMakeLists.txt
new file mode 100644
index 00000000..33e3e046
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/config/quectel/CMakeLists.txt
@@ -0,0 +1,32 @@
+# Copyright (C) 2022 Tencent Technologies Limited.
+# All rights reserved.
+#
+
+set(target ql_qcloud_iot)
+
+add_library(${target} STATIC)
+set_target_properties(${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${out_app_lib_dir})
+target_compile_definitions(${target} PRIVATE OSI_LOG_TAG=LOG_TAG_QCLOUD_IOT)
+
+set(PLATFORM "quectel")
+set(IOT_SDK_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/iot_c_sdk")
+include(${CMAKE_CURRENT_SOURCE_DIR}/config/iot_ql_sdk_setting.cmake)
+
+target_include_directories(${target} PUBLIC
+ ${IOT_SDK_SOURCE_DIR}/include/
+ ${IOT_SDK_SOURCE_DIR}/include/common
+ ${IOT_SDK_SOURCE_DIR}/include/config
+ ${IOT_SDK_SOURCE_DIR}/include/services/common
+ ${IOT_SDK_SOURCE_DIR}/include/services/hub
+ ${IOT_SDK_SOURCE_DIR}/include/services/explorer
+ ${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/mbedtls/include
+ ${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/port/inc
+ ${inc_mbedtls_port}
+ ${inc_common}
+ ${inc_platform}
+ ${inc_services}
+)
+target_sources(${target} PRIVATE qcloud_iot_mqtt_demo.c ${src_mbedtls_port} ${src_common} ${src_platform} ${src_services})
+
+relative_glob(srcs include/*.h src/*.c inc/*.h)
+beautify_c_code(${target} ${srcs})
diff --git a/components/connectivity/iot-hub-device-c-sdk/config/quectel/config/iot_ql_sdk_setting.cmake b/components/connectivity/iot-hub-device-c-sdk/config/quectel/config/iot_ql_sdk_setting.cmake
new file mode 100644
index 00000000..10425c34
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/config/quectel/config/iot_ql_sdk_setting.cmake
@@ -0,0 +1,146 @@
+###################### CONFIG #####################################
+
+# 开启单元测试
+set(CONFIG_IOT_TEST OFF)
+
+# 开启示例编译
+set(CONFIG_COMPILE_SAMPLE OFF)
+
+# 打开IOT DEBUG
+set(CONFIG_IOT_DEBUG OFF)
+
+# 代码抽取,ON表示根据配置抽取源码到ouput/sdk目录
+set(CONFIG_EXTRACT_SRC OFF)
+
+# 接入认证方式,使用证书认证:CERT;使用密钥认证:KEY
+set(CONFIG_AUTH_MODE "KEY")
+
+# 接入认证是否不使用TLS,证书方式必须选择使用TLS,密钥认证可选择不使用TLS
+set(CONFIG_AUTH_WITH_NOTLS OFF)
+
+# 使用用户自己的mbedtls库
+set(CONFIG_USR_MBEDTLS ON)
+
+# 是否打开代码中获取设备信息功能,OFF时将从device_info.json中读取设备信息
+set(CONFIG_DEBUG_DEV_INFO_USED ON)
+
+# 是否使能多线程
+set(CONFIG_MULTITHREAD_ENABLED ON)
+
+# 使用JSON格式上报日志 默认使用text格式
+set(CONFIG_LOG_UPLOAD_TYPE_JSON ON)
+
+# 使用AES加密上报日志
+set(CONFIG_LOG_UPLOAD_AES_ENCRYPT_POST OFF)
+
+# 是否使用动态注册功能
+set(CONFIG_DEV_DYN_REG_ENABLED ON)
+
+option(IOT_DEBUG "Enable IOT_DEBUG" ${CONFIG_IOT_DEBUG})
+option(DEBUG_DEV_INFO_USED "Enable DEBUG_DEV_INFO_USED" ${CONFIG_DEBUG_DEV_INFO_USED})
+option(AUTH_WITH_NO_TLS "Enable AUTH_WITH_NO_TLS" ${CONFIG_AUTH_WITH_NOTLS})
+option(LOG_UPLOAD_TYPE_JSON "Enable LOG_UPLOAD_DEBUG" ${CONFIG_LOG_UPLOAD_TYPE_JSON})
+option(LOG_UPLOAD_AES_ENCRYPT_POST "Log upload with AES encrypt" ${CONFIG_LOG_UPLOAD_AES_ENCRYPT_POST})
+option(DEV_DYN_REG_ENABLED "Enable DEV_DYN_REG_ENABLED" ${CONFIG_DEV_DYN_REG_ENABLED})
+option(MULTITHREAD_ENABLED "Enable AUTH_WITH_NO_TLS" ${CONFIG_MULTITHREAD_ENABLED})
+
+if(${CONFIG_AUTH_MODE} STREQUAL "KEY")
+ option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" ON)
+ option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" OFF)
+elseif(${CONFIG_AUTH_MODE} STREQUAL "CERT" AND ${CONFIG_AUTH_WITH_NOTLS} STREQUAL "OFF")
+ option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" OFF)
+ option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" ON)
+else()
+ message(FATAL_ERROR "INVAILD AUTH_MODE:${FEATURE_AUTH_MODE} WITH AUTH_WITH_NO_TLS:${FEATURE_AUTH_WITH_NOTLS}!")
+endif()
+
+configure_file (
+ "${IOT_SDK_SOURCE_DIR}/config/settings/qcloud_iot_config.h.in"
+ "${IOT_SDK_SOURCE_DIR}/include/config/qcloud_iot_config.h"
+ @ONLY
+)
+
+# export include
+include_directories(
+ ${IOT_SDK_SOURCE_DIR}/include/
+ ${IOT_SDK_SOURCE_DIR}/include/common
+ ${IOT_SDK_SOURCE_DIR}/include/config
+ ${IOT_SDK_SOURCE_DIR}/include/services/common
+ ${IOT_SDK_SOURCE_DIR}/include/services/hub
+ ${IOT_SDK_SOURCE_DIR}/include/services/explorer
+)
+
+###################### PLATFORM MODULE #######################################
+set(src_platform CACHE INTERNAL "")
+set(inc_platform CACHE INTERNAL "")
+
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/platform)
+
+# set include
+include_directories(${inc_platform})
+
+###################### COMMON MODULE #######################################
+set(src_common CACHE INTERNAL "")
+set(inc_common CACHE INTERNAL "")
+
+# mqtt packet
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/mqtt_packet)
+
+# utils
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/utils)
+
+# cryptology
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/cryptology)
+
+# set include
+include_directories(${inc_common})
+
+###################### SERVICE MODULE ####################################
+set(src_services CACHE INTERNAL "")
+set(inc_services CACHE INTERNAL "")
+
+# mqtt client (must include except dynamic register)
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/mqtt_client)
+
+## MQTT ONLY
+# 是否打开广播功能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/hub/broadcast)
+
+# 是否使能获取iot后台时间功能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/system)
+
+## HTTP ONLY
+
+# 是否使能设备动态注册
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/dynreg)
+
+## MQTT & HTTP
+
+# 是否使能网关功能
+#add_subdirectory()
+
+# 是否使能OTA固件升级功能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/ota)
+
+# 是否使能资源管理功能
+#add_subdirectory()
+
+# 是否使能HTTP请求腾讯云后台功能 如果开启了日志上报和动态注册 则必须要使能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/http_signed)
+
+# 是否使能日志上报云端功能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/log_upload)
+
+# set include
+include_directories(${inc_services})
+
+###################### MBEDTLS MODULE ####################################
+if(${CONFIG_AUTH_WITH_NOTLS} STREQUAL "OFF" AND ${CONFIG_USR_MBEDTLS} STREQUAL "OFF")
+ add_definitions("-DMBEDTLS_CONFIG_FILE=\"qcloud_iot_tls_psk_config.h\"")
+ add_subdirectory(${IOT_SDK_SOURCE_DIR}/3rd/mbedtls)
+ set(src_mbedtls_port "")
+ set(int_mbedtls_port "")
+else(${CONFIG_USR_MBEDTLS} STREQUAL "ON")
+ set(src_mbedtls_port ${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/port/src/qcloud_iot_tls_client.c)
+ set(int_mbedtls_port ${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/port/inc/)
+endif()
diff --git a/components/connectivity/iot-hub-device-c-sdk/config/quectel/qcloud_iot_mqtt_demo.c b/components/connectivity/iot-hub-device-c-sdk/config/quectel/qcloud_iot_mqtt_demo.c
new file mode 100644
index 00000000..163e8fe2
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/config/quectel/qcloud_iot_mqtt_demo.c
@@ -0,0 +1,278 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file mqtt_sample.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-03-08
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-03-08 1.0 fancyxu first commit
+ *
+ */
+
+#include
+#include
+#include
+#include
+
+#include "qcloud_iot_common.h"
+
+#include "utils_log.h"
+
+/**
+ * @brief MQTT event callback, @see MQTTEventHandleFun
+ *
+ * @param[in] client pointer to mqtt client
+ * @param[in] handle_context context
+ * @param[in] msg msg
+ */
+static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
+{
+ MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
+ uintptr_t packet_id = (uintptr_t)msg->msg;
+
+ switch (msg->event_type) {
+ case MQTT_EVENT_UNDEF:
+ Log_i("undefined event occur.");
+ break;
+
+ case MQTT_EVENT_DISCONNECT:
+ Log_i("MQTT disconnect.");
+ break;
+
+ case MQTT_EVENT_RECONNECT:
+ Log_i("MQTT reconnect.");
+ break;
+
+ case MQTT_EVENT_PUBLISH_RECEIVED:
+ Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
+ mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
+ mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
+ break;
+ case MQTT_EVENT_SUBSCRIBE_SUCCESS:
+ Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
+ Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_SUBSCRIBE_NACK:
+ Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
+ Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
+ Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_UNSUBSCRIBE_NACK:
+ Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_PUBLISH_SUCCESS:
+ Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_PUBLISH_TIMEOUT:
+ Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_PUBLISH_NACK:
+ Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
+ break;
+ default:
+ Log_i("Should NOT arrive here.");
+ break;
+ }
+}
+
+/**
+ * @brief Setup MQTT construct parameters.
+ *
+ * @param[in,out] initParams @see MQTTInitParams
+ * @param[in] device_info @see DeviceInfo
+ */
+static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
+{
+ init_params->device_info = device_info;
+ init_params->event_handle.h_fp = _mqtt_event_handler;
+}
+
+/**
+ * @brief Publish a test mqtt message.
+ *
+ * @param[in, out] client pointer to mqtt client
+ * @param[in] topic_keyword topic keyword
+ * @param[in] qos qos to publish
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+static int _publish_test_msg(void *client, const char *topic_keyword, QoS qos)
+{
+ char topic_name[MAX_SIZE_OF_CLOUD_TOPIC] = {0};
+
+ DeviceInfo *dev_info = IOT_MQTT_GetDeviceInfo(client);
+
+ HAL_Snprintf(topic_name, sizeof(topic_name), "%s/%s/%s", STRING_PTR_PRINT_SANITY_CHECK(dev_info->product_id),
+ STRING_PTR_PRINT_SANITY_CHECK(dev_info->device_name), STRING_PTR_PRINT_SANITY_CHECK(topic_keyword));
+
+ static int test_count = 0;
+ PublishParams pub_params = DEFAULT_PUB_PARAMS;
+ pub_params.qos = qos;
+
+ char publish_content[128] = {0};
+ HAL_Snprintf(publish_content, sizeof(publish_content), "{\"action\": \"publish_test\", \"count\": \"%d\"}",
+ test_count++);
+ pub_params.payload = publish_content;
+ pub_params.payload_len = strlen(publish_content);
+ return IOT_MQTT_Publish(client, topic_name, &pub_params);
+}
+
+/**
+ * @brief Callback when MQTT msg arrives @see OnMessageHandler
+ *
+ * @param[in, out] client pointer to mqtt client
+ * @param[in] message publish message from server
+ * @param[in] usr_data user data of SubscribeParams, @see SubscribeParams
+ */
+static void _on_message_callback(void *client, const MQTTMessage *message, void *user_data)
+{
+ if (!message) {
+ return;
+ }
+
+ Log_i("Receive Message With topicName:%.*s, payload:%.*s", message->topic_len,
+ STRING_PTR_PRINT_SANITY_CHECK(message->topic_name), message->payload_len,
+ STRING_PTR_PRINT_SANITY_CHECK(message->payload_str));
+}
+
+/**
+ * @brief Subscribe MQTT topic and wait for sub result.
+ *
+ * @param[in, out] client pointer to mqtt client
+ * @param[in] topic_keyword topic keyword
+ * @param[in] qos qos to subscribe
+ * @return @see IotReturnCode
+ */
+static int _subscribe_topic_wait_result(void *client, char *topic_keyword, QoS qos)
+{
+ char topic_name[MAX_SIZE_OF_CLOUD_TOPIC] = {0};
+
+ DeviceInfo *dev_info = IOT_MQTT_GetDeviceInfo(client);
+
+ HAL_Snprintf(topic_name, sizeof(topic_name), "%s/%s/%s", STRING_PTR_PRINT_SANITY_CHECK(dev_info->product_id),
+ STRING_PTR_PRINT_SANITY_CHECK(dev_info->device_name), STRING_PTR_PRINT_SANITY_CHECK(topic_keyword));
+
+ SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
+ sub_params.qos = qos;
+ sub_params.on_message_handler = _on_message_callback;
+
+ return IOT_MQTT_SubscribeSync(client, topic_name, &sub_params);
+}
+
+/**
+ * @brief Unsubscribe MQTT topic.
+ *
+ * @param[in, out] client pointer to mqtt client
+ * @param[in] topic_keyword topic keyword
+ * @return @see IotReturnCode
+ */
+static int _unsubscribe_topic(void *client, char *topic_keyword)
+{
+ char topic_name[MAX_SIZE_OF_CLOUD_TOPIC] = {0};
+
+ DeviceInfo *dev_info = IOT_MQTT_GetDeviceInfo(client);
+
+ HAL_Snprintf(topic_name, sizeof(topic_name), "%s/%s/%s", STRING_PTR_PRINT_SANITY_CHECK(dev_info->product_id),
+ STRING_PTR_PRINT_SANITY_CHECK(dev_info->device_name), STRING_PTR_PRINT_SANITY_CHECK(topic_keyword));
+
+ int rc = IOT_MQTT_Unsubscribe(client, topic_name);
+ if (rc < 0) {
+ Log_e("MQTT unsubscribe FAILED: %d", rc);
+ return rc;
+ }
+
+ return IOT_MQTT_Yield(client, 500); // wait for unsuback
+}
+
+// ----------------------------------------------------------------------------
+// Main
+// ----------------------------------------------------------------------------
+
+int qcloud_iot_mqtt_demo(int argc, char **argv)
+{
+ int rc;
+
+ // init log level
+ LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
+ utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
+
+ DeviceInfo device_info;
+
+ rc = HAL_GetDevInfo(&device_info);
+ if (rc) {
+ Log_e("get device info failed: %d", rc);
+ return rc;
+ }
+
+ // init connection
+ MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
+ _setup_connect_init_params(&init_params, &device_info);
+
+ // create MQTT client and connect with server
+ void *client = IOT_MQTT_Construct(&init_params);
+ if (client) {
+ Log_i("Cloud Device Construct Success");
+ } else {
+ Log_e("MQTT Construct failed!");
+ return QCLOUD_ERR_FAILURE;
+ }
+
+ // subscribe normal topics and wait result
+ rc = _subscribe_topic_wait_result(client, "data", QOS0);
+ if (rc) {
+ Log_e("Client Subscribe Topic Failed: %d", rc);
+ return rc;
+ }
+
+ do {
+ rc = _publish_test_msg(client, "data", QOS1);
+ if (rc < 0) {
+ Log_e("client publish topic failed :%d.", rc);
+ }
+
+ rc = IOT_MQTT_Yield(client, 2000);
+ if (rc == QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT) {
+ HAL_SleepMs(1000);
+ continue;
+ } else if (rc != QCLOUD_RET_SUCCESS && rc != QCLOUD_RET_MQTT_RECONNECTED) {
+ Log_e("exit with error: %d", rc);
+ break;
+ }
+ } while (1);
+
+ rc = _unsubscribe_topic(client, "data");
+ rc |= IOT_MQTT_Destroy(&client);
+
+ utils_log_deinit();
+ return rc;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/config/quectel/readme.md b/components/connectivity/iot-hub-device-c-sdk/config/quectel/readme.md
new file mode 100644
index 00000000..07dd3fc3
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/config/quectel/readme.md
@@ -0,0 +1,57 @@
+# 移远SDK适配
+
+1. 将该目录的内容拷贝到`ql-application\qcloud-iot-c-sdk`下,并将sdk放置在`ql-application\qcloud-iot-c-sdk\iot_c_sdk`目录下
+
+```c
+├── CMakeLists.txt
+├── config
+├── iot_c_sdk
+└── qcloud_iot_mqtt_demo.c
+```
+
+2. 修改相应的编译脚本
+
+- `core_export.list` 添加相应的函数
+
+ ```c
+ //md5
+ mbedtls_md5_init
+ mbedtls_md5_starts
+ mbedtls_md5_update
+ mbedtls_md5_finish
+
+ //
+ mbedtls_ctr_drbg_free
+ mbedtls_entropy_free
+ mbedtls_ctr_drbg_init
+ mbedtls_entropy_init
+ mbedtls_ctr_drbg_seed
+ mbedtls_entropy_func
+ mbedtls_ctr_drbg_random
+ ```
+
+- `ql-application/CMakeLists.txt`修改
+
+ ```cmake
+ if(QL_APP_FEATURE_QCLOUD_IOT)
+ add_subdirectory_if_exist(qcloud-iot-c-sdk)
+ endif()
+ ```
+
+- `ql-application/init/ql_init.c`修改
+
+ ```c
+ #ifdef QL_APP_FEATURE_QCLOUD_IOT
+ #include "qcloud_iot_common.h"
+ #include "qcloud_iot_hub.h"
+ #include "qcloud_iot_explorer.h"
+ #endif
+ ```
+
+ ```c
+ #ifdef QL_APP_FEATURE_QCLOUD_IOT
+ //your application entry
+ extern int qcloud_iot_mqtt_demo(int argc, char **argv);
+ qcloud_iot_mqtt_demo(0, NULL);
+ #endif
+ ```
diff --git a/components/connectivity/iot-hub-device-c-sdk/config/settings/iot_app_all.cmake b/components/connectivity/iot-hub-device-c-sdk/config/settings/iot_app_all.cmake
new file mode 100644
index 00000000..b68aea6c
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/config/settings/iot_app_all.cmake
@@ -0,0 +1,157 @@
+# ##################### CONFIG #####################################
+
+# 开启单元测试
+set(CONFIG_IOT_TEST OFF)
+
+# 开启示例编译
+set(CONFIG_COMPILE_SAMPLE ON)
+
+# 打开IOT DEBUG
+set(CONFIG_IOT_DEBUG OFF)
+
+# 代码抽取,ON表示根据配置抽取源码到ouput/sdk目录
+set(CONFIG_EXTRACT_SRC ON)
+
+# 接入认证方式,使用证书认证:CERT;使用密钥认证:KEY
+set(CONFIG_AUTH_MODE "KEY")
+
+# 接入认证是否不使用TLS,证书方式必须选择使用TLS,密钥认证可选择不使用TLS
+set(CONFIG_AUTH_WITH_NOTLS OFF)
+
+# 是否打开代码中获取设备信息功能,OFF时将从device_info.json中读取设备信息
+set(CONFIG_DEBUG_DEV_INFO_USED ON)
+
+# 是否使能多线程
+set(CONFIG_MULTITHREAD_ENABLED OFF)
+
+# 使用JSON格式上报日志 默认使用text格式
+set(CONFIG_LOG_UPLOAD_TYPE_JSON OFF)
+
+# 使用AES加密上报日志
+set(CONFIG_LOG_UPLOAD_AES_ENCRYPT_POST OFF)
+
+# 是否使用动态注册功能
+set(CONFIG_DEV_DYN_REG_ENABLED ON)
+
+# 是否打开获取iot后台时间功能
+# Whether to access the feature of getting iot background time
+set(FEATURE_SYSTEM_COMM_ENABLED ON)
+
+option(IOT_DEBUG "Enable IOT_DEBUG" ${CONFIG_IOT_DEBUG})
+option(DEBUG_DEV_INFO_USED "Enable DEBUG_DEV_INFO_USED" ${CONFIG_DEBUG_DEV_INFO_USED})
+option(AUTH_WITH_NO_TLS "Enable AUTH_WITH_NO_TLS" ${CONFIG_AUTH_WITH_NOTLS})
+option(LOG_UPLOAD_TYPE_JSON "Enable LOG_UPLOAD_DEBUG" ${CONFIG_LOG_UPLOAD_TYPE_JSON})
+option(LOG_UPLOAD_AES_ENCRYPT_POST "Log upload with AES encrypt" ${CONFIG_LOG_UPLOAD_AES_ENCRYPT_POST})
+option(SYSTEM_COMM "Enable SYSTEM_COMM" ${FEATURE_SYSTEM_COMM_ENABLED})
+option(DEV_DYN_REG_ENABLED "Enable DEV_DYN_REG_ENABLED" ${CONFIG_DEV_DYN_REG_ENABLED})
+
+if(${CONFIG_AUTH_MODE} STREQUAL "KEY")
+ option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" ON)
+ option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" OFF)
+elseif(${CONFIG_AUTH_MODE} STREQUAL "CERT" AND ${CONFIG_AUTH_WITH_NOTLS} STREQUAL "OFF")
+ option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" OFF)
+ option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" ON)
+else()
+ message(FATAL_ERROR "INVAILD AUTH_MODE:${FEATURE_AUTH_MODE} WITH AUTH_WITH_NO_TLS:${FEATURE_AUTH_WITH_NOTLS}!")
+endif()
+
+configure_file(
+ "${IOT_SDK_SOURCE_DIR}/config/settings/qcloud_iot_config.h.in"
+ "${IOT_SDK_SOURCE_DIR}/include/config/qcloud_iot_config.h"
+ @ONLY
+)
+
+# export include
+include_directories(
+ ${IOT_SDK_SOURCE_DIR}/include/
+ ${IOT_SDK_SOURCE_DIR}/include/common
+ ${IOT_SDK_SOURCE_DIR}/include/config
+ ${IOT_SDK_SOURCE_DIR}/include/services/common
+ ${IOT_SDK_SOURCE_DIR}/include/services/explorer
+)
+
+# set output path
+set(LIBRARY_OUTPUT_PATH ${IOT_SDK_SOURCE_DIR}/output/libs)
+set(EXECUTABLE_OUTPUT_PATH ${IOT_SDK_SOURCE_DIR}/output/bin)
+
+# set link lib dir
+link_directories(${LIBRARY_OUTPUT_PATH})
+
+# set test src
+if(${CONFIG_IOT_TEST} STREQUAL "ON")
+ set(src_test CACHE INTERNAL "")
+ set(inc_test CACHE INTERNAL "")
+endif()
+
+# ##################### PLATFORM MODULE #######################################
+set(src_platform CACHE INTERNAL "")
+set(inc_platform CACHE INTERNAL "")
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/platform)
+include_directories(${inc_platform})
+add_library(iot_platform STATIC ${src_platform})
+
+# ##################### COMMON MODULE #######################################
+set(src_common CACHE INTERNAL "")
+set(inc_common CACHE INTERNAL "")
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/mqtt_packet)
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/utils)
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/cryptology)
+include_directories(${inc_common})
+add_library(iot_common STATIC ${src_common})
+
+# ##################### 3rd MODULE ####################################
+# mbedtls
+if(${CONFIG_AUTH_MODE} STREQUAL "KEY")
+ include_directories(
+ ${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/mbedtls/include
+ ${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/port/inc
+ )
+ add_definitions("-DMBEDTLS_CONFIG_FILE=\"qcloud_iot_tls_psk_config.h\"")
+endif()
+
+if(${CONFIG_AUTH_WITH_NOTLS} STREQUAL "OFF")
+ add_subdirectory(${IOT_SDK_SOURCE_DIR}/3rd/mbedtls)
+ set(libsdk ${libsdk} mbedtls)
+endif()
+
+# ##################### SERVICE MODULE ####################################
+set(src_services CACHE INTERNAL "")
+set(inc_services CACHE INTERNAL "")
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/mqtt_client)
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/http_client)
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/http_signed)
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/cos)
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/system)
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/log_upload)
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/dynreg)
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/ota)
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/gateway)
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/explorer/data_template)
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/explorer/service_mqtt)
+include_directories(${inc_services})
+add_library(iot_services STATIC ${src_services})
+
+# ##################### APP ####################################
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/app/data_template)
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/app/ota)
+
+# ##################### EXTRACT ####################################
+if(${CONFIG_EXTRACT_SRC} STREQUAL "ON")
+ file(COPY ${src_platform} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
+ file(COPY ${inc_platform} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
+
+ file(COPY ${src_common} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
+ file(COPY ${inc_common} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
+
+ file(COPY ${src_services} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
+ file(COPY ${inc_services} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
+
+ file(GLOB inc_export
+ ${IOT_SDK_SOURCE_DIR}/include/*.h
+ ${IOT_SDK_SOURCE_DIR}/include/common/*.h
+ ${IOT_SDK_SOURCE_DIR}/include/config/*.h
+ ${IOT_SDK_SOURCE_DIR}/include/services/common/*.h
+ ${IOT_SDK_SOURCE_DIR}/include/services/explorer/*.h
+ )
+ file(COPY ${inc_export} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc)
+endif()
diff --git a/components/connectivity/iot-hub-device-c-sdk/config/settings/iot_explorer_at_module.cmake b/components/connectivity/iot-hub-device-c-sdk/config/settings/iot_explorer_at_module.cmake
new file mode 100644
index 00000000..fd8dee24
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/config/settings/iot_explorer_at_module.cmake
@@ -0,0 +1,127 @@
+# ##################### CONFIG #####################################
+
+# 开启单元测试
+set(CONFIG_IOT_TEST OFF)
+
+# 开启示例编译
+set(CONFIG_COMPILE_SAMPLE OFF)
+
+# 打开IOT DEBUG
+set(CONFIG_IOT_DEBUG OFF)
+
+# 代码抽取,ON表示根据配置抽取源码到ouput/sdk目录
+set(CONFIG_EXTRACT_SRC ON)
+
+# 接入认证方式,使用证书认证:CERT;使用密钥认证:KEY
+set(CONFIG_AUTH_MODE "KEY")
+
+# 接入认证是否不使用TLS,证书方式必须选择使用TLS,密钥认证可选择不使用TLS
+set(CONFIG_AUTH_WITH_NOTLS ON)
+
+# 是否打开代码中获取设备信息功能,OFF时将从device_info.json中读取设备信息
+set(CONFIG_DEBUG_DEV_INFO_USED ON)
+
+# 是否使能多线程
+set(CONFIG_MULTITHREAD_ENABLED ON)
+
+# 是否使能AT模组
+set(CONFIG_AT_MODULE_ENABLED ON)
+
+option(IOT_DEBUG "Enable IOT_DEBUG" ${CONFIG_IOT_DEBUG})
+option(DEBUG_DEV_INFO_USED "Enable DEBUG_DEV_INFO_USED" ${CONFIG_DEBUG_DEV_INFO_USED})
+option(AUTH_WITH_NO_TLS "Enable AUTH_WITH_NO_TLS" ${CONFIG_AUTH_WITH_NOTLS})
+option(MULTITHREAD_ENABLED "Enable AUTH_WITH_NO_TLS" ${CONFIG_MULTITHREAD_ENABLED})
+option(AT_MODULE_ENABLE "Enable AUTH_WITH_NO_TLS" ${CONFIG_AT_MODULE_ENABLED})
+
+if(${CONFIG_AUTH_MODE} STREQUAL "KEY")
+ option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" ON)
+ option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" OFF)
+elseif(${CONFIG_AUTH_MODE} STREQUAL "CERT" AND ${CONFIG_AUTH_WITH_NOTLS} STREQUAL "OFF")
+ option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" OFF)
+ option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" ON)
+else()
+ message(FATAL_ERROR "INVAILD AUTH_MODE:${FEATURE_AUTH_MODE} WITH AUTH_WITH_NO_TLS:${FEATURE_AUTH_WITH_NOTLS}!")
+endif()
+
+configure_file(
+ "${IOT_SDK_SOURCE_DIR}/config/settings/qcloud_iot_config.h.in"
+ "${IOT_SDK_SOURCE_DIR}/include/config/qcloud_iot_config.h"
+ @ONLY
+)
+
+# export include
+include_directories(
+ ${IOT_SDK_SOURCE_DIR}/include/
+ ${IOT_SDK_SOURCE_DIR}/include/common
+ ${IOT_SDK_SOURCE_DIR}/include/config
+ ${IOT_SDK_SOURCE_DIR}/include/services/common
+ ${IOT_SDK_SOURCE_DIR}/include/services/explorer
+)
+
+# set output path
+set(LIBRARY_OUTPUT_PATH ${IOT_SDK_SOURCE_DIR}/output/libs)
+set(EXECUTABLE_OUTPUT_PATH ${IOT_SDK_SOURCE_DIR}/output/bin)
+
+# set link lib dir
+link_directories(${LIBRARY_OUTPUT_PATH})
+
+# set test src
+if(${CONFIG_IOT_TEST} STREQUAL "ON")
+ set(src_test CACHE INTERNAL "")
+ set(inc_test CACHE INTERNAL "")
+endif()
+
+# ##################### PLATFORM MODULE #######################################
+set(src_platform CACHE INTERNAL "")
+set(inc_platform CACHE INTERNAL "")
+
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/platform)
+
+# add_subdirectory(${IOT_SDK_SOURCE_DIR}/platform/at_client)
+
+# ##################### COMMON MODULE #######################################
+set(src_common CACHE INTERNAL "")
+set(inc_common CACHE INTERNAL "")
+
+# utils
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/utils)
+
+# cryptology
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/cryptology)
+
+# ##################### SERVICE MODULE ####################################
+set(src_services CACHE INTERNAL "")
+set(inc_services CACHE INTERNAL "")
+
+# mqtt client
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/at_module)
+
+# 是否使能获取iot后台时间功能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/system)
+
+# 是否使能数据模板功能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/explorer/data_template)
+
+# 是否使能网关功能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/gateway)
+
+# ##################### EXTRACT ####################################
+if(${CONFIG_EXTRACT_SRC} STREQUAL "ON")
+ file(COPY ${src_platform} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
+ file(COPY ${inc_platform} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
+
+ file(COPY ${src_common} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
+ file(COPY ${inc_common} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
+
+ file(COPY ${src_services} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
+ file(COPY ${inc_services} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
+
+ file(GLOB inc_export
+ ${IOT_SDK_SOURCE_DIR}/include/*.h
+ ${IOT_SDK_SOURCE_DIR}/include/common/*.h
+ ${IOT_SDK_SOURCE_DIR}/include/config/*.h
+ ${IOT_SDK_SOURCE_DIR}/include/services/common/*.h
+ ${IOT_SDK_SOURCE_DIR}/include/services/explorer/*.h
+ )
+ file(COPY ${inc_export} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc)
+endif()
diff --git a/components/connectivity/iot-hub-device-c-sdk/config/settings/iot_explorer_setting.cmake b/components/connectivity/iot-hub-device-c-sdk/config/settings/iot_explorer_setting.cmake
new file mode 100644
index 00000000..ab8f7541
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/config/settings/iot_explorer_setting.cmake
@@ -0,0 +1,213 @@
+###################### CONFIG #####################################
+
+# 开启单元测试
+set(CONFIG_IOT_TEST OFF)
+
+# 开启示例编译
+set(CONFIG_COMPILE_SAMPLE ON)
+
+# 打开IOT DEBUG
+set(CONFIG_IOT_DEBUG OFF)
+
+# 代码抽取,ON表示根据配置抽取源码到ouput/sdk目录
+set(CONFIG_EXTRACT_SRC OFF)
+
+# 接入认证方式,使用证书认证:CERT;使用密钥认证:KEY
+set(CONFIG_AUTH_MODE "KEY")
+
+# 接入认证是否不使用TLS,证书方式必须选择使用TLS,密钥认证可选择不使用TLS
+set(CONFIG_AUTH_WITH_NOTLS OFF)
+
+# 是否打开代码中获取设备信息功能,OFF时将从device_info.json中读取设备信息
+set(CONFIG_DEBUG_DEV_INFO_USED ON)
+
+# 是否使能多线程
+set(CONFIG_MULTITHREAD_ENABLED ON)
+
+# 使用JSON格式上报日志 默认使用text格式
+set(CONFIG_LOG_UPLOAD_TYPE_JSON ON)
+
+# 使用AES加密上报日志
+set(CONFIG_LOG_UPLOAD_AES_ENCRYPT_POST OFF)
+
+# 是否使用动态注册功能
+set(CONFIG_DEV_DYN_REG_ENABLED ON)
+
+option(IOT_DEBUG "Enable IOT_DEBUG" ${CONFIG_IOT_DEBUG})
+option(DEBUG_DEV_INFO_USED "Enable DEBUG_DEV_INFO_USED" ${CONFIG_DEBUG_DEV_INFO_USED})
+option(AUTH_WITH_NO_TLS "Enable AUTH_WITH_NO_TLS" ${CONFIG_AUTH_WITH_NOTLS})
+option(LOG_UPLOAD_TYPE_JSON "Enable LOG_UPLOAD_DEBUG" ${CONFIG_LOG_UPLOAD_TYPE_JSON})
+option(LOG_UPLOAD_AES_ENCRYPT_POST "Log upload with AES encrypt" ${CONFIG_LOG_UPLOAD_AES_ENCRYPT_POST})
+option(DEV_DYN_REG_ENABLED "Enable DEV_DYN_REG_ENABLED" ${CONFIG_DEV_DYN_REG_ENABLED})
+option(MULTITHREAD_ENABLED "Enable AUTH_WITH_NO_TLS" ${CONFIG_MULTITHREAD_ENABLED})
+
+if(${CONFIG_AUTH_MODE} STREQUAL "KEY")
+ option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" ON)
+ option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" OFF)
+elseif(${CONFIG_AUTH_MODE} STREQUAL "CERT" AND ${CONFIG_AUTH_WITH_NOTLS} STREQUAL "OFF")
+ option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" OFF)
+ option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" ON)
+else()
+ message(FATAL_ERROR "INVAILD AUTH_MODE:${FEATURE_AUTH_MODE} WITH AUTH_WITH_NO_TLS:${FEATURE_AUTH_WITH_NOTLS}!")
+endif()
+
+configure_file (
+ "${IOT_SDK_SOURCE_DIR}/config/settings/qcloud_iot_config.h.in"
+ "${IOT_SDK_SOURCE_DIR}/include/config/qcloud_iot_config.h"
+ @ONLY
+)
+
+# export include
+include_directories(
+ ${IOT_SDK_SOURCE_DIR}/include/
+ ${IOT_SDK_SOURCE_DIR}/include/common
+ ${IOT_SDK_SOURCE_DIR}/include/config
+ ${IOT_SDK_SOURCE_DIR}/include/services/common
+ ${IOT_SDK_SOURCE_DIR}/include/services/explorer
+)
+
+# set output path
+set(LIBRARY_OUTPUT_PATH ${IOT_SDK_SOURCE_DIR}/output/libs)
+set(EXECUTABLE_OUTPUT_PATH ${IOT_SDK_SOURCE_DIR}/output/bin)
+
+# set link lib dir
+link_directories(${LIBRARY_OUTPUT_PATH})
+
+# set test src
+if(${CONFIG_IOT_TEST} STREQUAL "ON")
+ set(src_test CACHE INTERNAL "")
+ set(inc_test CACHE INTERNAL "")
+endif()
+
+###################### PLATFORM MODULE #######################################
+set(src_platform CACHE INTERNAL "")
+set(inc_platform CACHE INTERNAL "")
+
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/platform)
+
+# set include
+include_directories(${inc_platform})
+
+# add library libiot_platform.a
+add_library(iot_platform STATIC ${src_platform})
+
+###################### COMMON MODULE #######################################
+set(src_common CACHE INTERNAL "")
+set(inc_common CACHE INTERNAL "")
+
+# mqtt packet
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/mqtt_packet)
+
+# utils
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/utils)
+
+# cryptology
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/cryptology)
+
+# set include
+include_directories(${inc_common})
+
+# add library libiot_common.a
+add_library(iot_common STATIC ${src_common})
+
+###################### 3rd MODULE ####################################
+
+# mbedtls
+if(${CONFIG_AUTH_MODE} STREQUAL "KEY" )
+ include_directories(
+ ${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/mbedtls/include
+ ${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/port/inc
+ )
+ add_definitions("-DMBEDTLS_CONFIG_FILE=\"qcloud_iot_tls_psk_config.h\"")
+endif()
+
+if(${CONFIG_AUTH_WITH_NOTLS} STREQUAL "OFF")
+ # libmbedtls.a
+ add_subdirectory(${IOT_SDK_SOURCE_DIR}/3rd/mbedtls)
+ set(libsdk ${libsdk} mbedtls)
+endif()
+
+
+###################### SERVICE MODULE ####################################
+set(src_services CACHE INTERNAL "")
+set(inc_services CACHE INTERNAL "")
+
+# mqtt client (must include except dynamic register)
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/mqtt_client)
+
+# http client
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/http_client)
+
+## MQTT ONLY
+
+# 是否使能获取iot后台时间功能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/system)
+
+# 是否使能OTA固件升级功能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/ota)
+
+# gateway
+add_subdirectory(${PROJECT_SOURCE_DIR}/services/common/gateway)
+
+# 是否使能数据模板功能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/explorer/data_template)
+
+# 是否系统服务
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/explorer/service_mqtt)
+
+## HTTP ONLY
+
+# 是否使能设备动态注册
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/dynreg)
+
+# 是否使能COS下载功能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/cos)
+
+# 是否使能HTTP请求腾讯云后台功能 如果开启了日志上报和动态注册 则必须要使能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/http_signed)
+
+## HTTP & MQTT
+
+# 是否使能日志上报云端功能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/log_upload)
+
+# set include
+include_directories(${inc_services})
+
+# add library libiot_services.a
+add_library(iot_services STATIC ${src_services})
+
+###################### UINT TEST ####################################
+if(${CONFIG_IOT_TEST} STREQUAL "ON")
+ include_directories(${inc_test})
+ find_package(GTest REQUIRED)
+ add_executable(iot_hub_sdk_test ${src_test})
+ target_link_libraries(iot_hub_sdk_test ${GTEST_BOTH_LIBRARIES} ${libsdk})
+ setup_target_for_coverage_gcovr_html(
+ NAME sdk_test_coverage
+ EXECUTABLE iot_hub_sdk_test
+ DEPENDENCIES iot_hub_sdk_test
+ )
+endif()
+
+###################### EXTRACT ####################################
+
+if(${CONFIG_EXTRACT_SRC} STREQUAL "ON")
+ file(COPY ${src_platform} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
+ file(COPY ${inc_platform} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
+
+ file(COPY ${src_common} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
+ file(COPY ${inc_common} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
+
+ file(COPY ${src_services} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
+ file(COPY ${inc_services} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
+
+ file(GLOB inc_export
+ ${IOT_SDK_SOURCE_DIR}/include/*.h
+ ${IOT_SDK_SOURCE_DIR}/include/common/*.h
+ ${IOT_SDK_SOURCE_DIR}/include/config/*.h
+ ${IOT_SDK_SOURCE_DIR}/include/services/common/*.h
+ ${IOT_SDK_SOURCE_DIR}/include/services/explorer/*.h
+ )
+ file(COPY ${inc_export} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc)
+endif()
diff --git a/components/connectivity/iot-hub-device-c-sdk/config/settings/iot_hub_setting.cmake b/components/connectivity/iot-hub-device-c-sdk/config/settings/iot_hub_setting.cmake
new file mode 100644
index 00000000..84ca23bd
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/config/settings/iot_hub_setting.cmake
@@ -0,0 +1,205 @@
+###################### CONFIG #####################################
+
+# 开启单元测试
+set(CONFIG_IOT_TEST OFF)
+
+# 开启示例编译
+set(CONFIG_COMPILE_SAMPLE ON)
+
+# 打开IOT DEBUG
+set(CONFIG_IOT_DEBUG OFF)
+
+# 代码抽取,ON表示根据配置抽取源码到ouput/sdk目录
+set(CONFIG_EXTRACT_SRC OFF)
+
+# 接入认证方式,使用证书认证:CERT;使用密钥认证:KEY
+set(CONFIG_AUTH_MODE "KEY")
+
+# 接入认证是否不使用TLS,证书方式必须选择使用TLS,密钥认证可选择不使用TLS
+set(CONFIG_AUTH_WITH_NOTLS OFF)
+
+# 是否打开代码中获取设备信息功能,OFF时将从device_info.json中读取设备信息
+set(CONFIG_DEBUG_DEV_INFO_USED ON)
+
+# 是否使能多线程
+set(CONFIG_MULTITHREAD_ENABLED OFF)
+
+# 使用JSON格式上报日志 默认使用text格式
+set(CONFIG_LOG_UPLOAD_TYPE_JSON ON)
+
+option(IOT_DEBUG "Enable IOT_DEBUG" ${CONFIG_IOT_DEBUG})
+option(DEBUG_DEV_INFO_USED "Enable DEBUG_DEV_INFO_USED" ${CONFIG_DEBUG_DEV_INFO_USED})
+option(AUTH_WITH_NO_TLS "Enable AUTH_WITH_NO_TLS" ${CONFIG_AUTH_WITH_NOTLS})
+option(LOG_UPLOAD_TYPE_JSON "Enable LOG_UPLOAD_DEBUG" ${CONFIG_LOG_UPLOAD_TYPE_JSON})
+
+if(${CONFIG_AUTH_MODE} STREQUAL "KEY")
+ option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" ON)
+ option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" OFF)
+elseif(${CONFIG_AUTH_MODE} STREQUAL "CERT" AND ${CONFIG_AUTH_WITH_NOTLS} STREQUAL "OFF")
+ option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" OFF)
+ option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" ON)
+else()
+ message(FATAL_ERROR "INVAILD AUTH_MODE:${FEATURE_AUTH_MODE} WITH AUTH_WITH_NO_TLS:${FEATURE_AUTH_WITH_NOTLS}!")
+endif()
+
+configure_file (
+ "${IOT_SDK_SOURCE_DIR}/config/settings/qcloud_iot_config.h.in"
+ "${IOT_SDK_SOURCE_DIR}/include/config/qcloud_iot_config.h"
+ @ONLY
+)
+
+# export include
+include_directories(
+ ${IOT_SDK_SOURCE_DIR}/include/
+ ${IOT_SDK_SOURCE_DIR}/include/common
+ ${IOT_SDK_SOURCE_DIR}/include/config
+ ${IOT_SDK_SOURCE_DIR}/include/services/common
+ ${IOT_SDK_SOURCE_DIR}/include/services/hub
+)
+
+# set output path
+set(LIBRARY_OUTPUT_PATH ${IOT_SDK_SOURCE_DIR}/output/libs)
+set(EXECUTABLE_OUTPUT_PATH ${IOT_SDK_SOURCE_DIR}/output/bin)
+
+# set link lib dir
+link_directories(${LIBRARY_OUTPUT_PATH})
+
+# set test src
+if(${CONFIG_IOT_TEST} STREQUAL "ON")
+ set(src_test CACHE INTERNAL "")
+ set(inc_test CACHE INTERNAL "")
+endif()
+
+###################### PLATFORM MODULE #######################################
+set(src_platform CACHE INTERNAL "")
+set(inc_platform CACHE INTERNAL "")
+
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/platform)
+
+# set include
+include_directories(${inc_platform})
+
+# add library libiot_platform.a
+add_library(iot_platform STATIC ${src_platform})
+
+###################### COMMON MODULE #######################################
+set(src_common CACHE INTERNAL "")
+set(inc_common CACHE INTERNAL "")
+
+# mqtt packet
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/mqtt_packet)
+
+# utils
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/utils)
+
+# cryptology
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/cryptology)
+
+# set include
+include_directories(${inc_common})
+
+# add library libiot_common.a
+add_library(iot_common STATIC ${src_common})
+
+###################### SERVICE MODULE ####################################
+set(src_services CACHE INTERNAL "")
+set(inc_services CACHE INTERNAL "")
+
+# mqtt client (must include except dynamic register)
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/mqtt_client)
+
+## MQTT ONLY
+# 是否打开广播功能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/hub/broadcast)
+
+# 是否使能获取iot后台时间功能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/system)
+
+# 是否打开RRPC功能
+#add_subdirectory()
+
+# 是否打开远程配置功能
+#add_subdirectory()
+
+# 是否打开设备影子的总开关
+#add_subdirectory()
+
+## HTTP ONLY
+
+# 是否使能设备动态注册
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/dynreg)
+
+## MQTT & HTTP
+
+# 是否使能网关功能
+#add_subdirectory()
+
+# 是否使能OTA固件升级功能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/ota)
+
+# 是否使能资源管理功能
+#add_subdirectory()
+
+# 是否使能HTTP请求腾讯云后台功能 如果开启了日志上报和动态注册 则必须要使能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/http_signed)
+
+# 是否使能日志上报云端功能
+add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/log_upload)
+
+# set include
+include_directories(${inc_services})
+
+# add library libiot_services.a
+add_library(iot_services STATIC ${src_services})
+
+###################### 3rd MODULE ####################################
+
+# mbedtls
+if(${CONFIG_AUTH_MODE} STREQUAL "KEY" )
+ include_directories(
+ ${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/mbedtls/include
+ ${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/port/inc
+ )
+ add_definitions("-DMBEDTLS_CONFIG_FILE=\"qcloud_iot_tls_psk_config.h\"")
+endif()
+
+if(${CONFIG_AUTH_WITH_NOTLS} STREQUAL "OFF")
+ # libmbedtls.a
+ add_subdirectory(${IOT_SDK_SOURCE_DIR}/3rd/mbedtls)
+ set(libsdk ${libsdk} mbedtls)
+endif()
+
+###################### UINT TEST ####################################
+if(${CONFIG_IOT_TEST} STREQUAL "ON")
+ include_directories(${inc_test})
+ find_package(GTest REQUIRED)
+ add_executable(iot_hub_sdk_test ${src_test})
+ target_link_libraries(iot_hub_sdk_test ${GTEST_BOTH_LIBRARIES} ${libsdk})
+ setup_target_for_coverage_gcovr_html(
+ NAME sdk_test_coverage
+ EXECUTABLE iot_hub_sdk_test
+ DEPENDENCIES iot_hub_sdk_test
+ )
+endif()
+
+###################### EXTRACT ####################################
+
+if(${CONFIG_EXTRACT_SRC} STREQUAL "ON")
+ file(COPY ${src_platform} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
+ file(COPY ${inc_platform} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
+
+ file(COPY ${src_common} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
+ file(COPY ${inc_common} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
+
+ file(COPY ${src_services} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
+ file(COPY ${inc_services} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
+
+ file(GLOB inc_export
+ ${IOT_SDK_SOURCE_DIR}/include/*.h
+ ${IOT_SDK_SOURCE_DIR}/include/common/*.h
+ ${IOT_SDK_SOURCE_DIR}/include/config/*.h
+ ${IOT_SDK_SOURCE_DIR}/include/services/common/*.h
+ ${IOT_SDK_SOURCE_DIR}/include/services/explorer/*.h
+ )
+ file(COPY ${inc_export} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc)
+endif()
diff --git a/components/connectivity/iot-hub-device-c-sdk/config/settings/qcloud_iot_config.h.in b/components/connectivity/iot-hub-device-c-sdk/config/settings/qcloud_iot_config.h.in
new file mode 100644
index 00000000..446b862f
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/config/settings/qcloud_iot_config.h.in
@@ -0,0 +1,55 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_config.h
+ * @brief sdk config define
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-06-01
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-06-01 1.0 fancyxu first commit
+ * 2021-07-12 1.1 fancyxu rename AUTH_WITH_NOTLS to AUTH_WITH_NO_TLS
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_CONFIG_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_CONFIG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#cmakedefine AUTH_MODE_CERT
+#cmakedefine AUTH_MODE_KEY
+#cmakedefine AUTH_WITH_NO_TLS
+#cmakedefine GATEWAY_ENABLED
+#cmakedefine SYSTEM_COMM
+#cmakedefine DEV_DYN_REG_ENABLED
+#cmakedefine LOG_UPLOAD
+#cmakedefine IOT_DEBUG
+#cmakedefine DEBUG_DEV_INFO_USED
+#cmakedefine AT_MODULE_ENABLE
+#cmakedefine MULTITHREAD_ENABLED
+#cmakedefine LOG_UPLOAD_TYPE_JSON
+#cmakedefine LOG_UPLOAD_AES_ENCRYPT_POST
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_CONFIG_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/config/toolchains/free_rtos_none.cmake b/components/connectivity/iot-hub-device-c-sdk/config/toolchains/free_rtos_none.cmake
new file mode 100644
index 00000000..b6ff77fb
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/config/toolchains/free_rtos_none.cmake
@@ -0,0 +1,4 @@
+# 编译类型: release/debug
+set(BUILD_TYPE "debug")
+# 设置操作系统
+set(PLATFORM "free_rtos")
diff --git a/components/connectivity/iot-hub-device-c-sdk/config/toolchains/linux_x86_gcc.cmake b/components/connectivity/iot-hub-device-c-sdk/config/toolchains/linux_x86_gcc.cmake
new file mode 100644
index 00000000..3ff26b43
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/config/toolchains/linux_x86_gcc.cmake
@@ -0,0 +1,24 @@
+# 编译类型: release/debug
+set(BUILD_TYPE "debug")
+
+# 设置CMAKE使用编译工具及编译选项
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+set(CMAKE_C_COMPILER "/usr/bin/gcc")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Wall -pthread -fstack-protector-strong -Wl,-z,now -Wl,-z,noexecstack -fPIE -pie -ffunction-sections -fdata-sections")
+set(LINK_FLAGS "${LINK_FLAGS} -Wl,--gc-sections")
+
+if(${BUILD_TYPE} STREQUAL "debug")
+ set(CMAKE_BUILD_TYPE "Debug")
+ list(APPEND CMAKE_MODULE_PATH "${IOT_SDK_SOURCE_DIR}/config/gcovr")
+ include(code_coverage)
+ append_coverage_compiler_flags()
+else()
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Os -D_FORTIFY_SOURCE=2") # 编译选项 -Wl,-Map,iot.map
+endif()
+
+set(PLATFORM "linux")
+
+set(libsdk -Wl,--start-group ${libsdk} iot_common iot_services iot_platform pthread -Wl,--end-group)
diff --git a/components/connectivity/iot-hub-device-c-sdk/config/toolchains/tencentos_tiny_none.cmake b/components/connectivity/iot-hub-device-c-sdk/config/toolchains/tencentos_tiny_none.cmake
new file mode 100644
index 00000000..6206e893
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/config/toolchains/tencentos_tiny_none.cmake
@@ -0,0 +1,4 @@
+# 编译类型: release/debug
+set(BUILD_TYPE "debug")
+# 设置操作系统
+set(PLATFORM "tencentos_tiny")
diff --git a/components/connectivity/iot-hub-device-c-sdk/config/tools/data_template_codegen.py b/components/connectivity/iot-hub-device-c-sdk/config/tools/data_template_codegen.py
new file mode 100644
index 00000000..b6340662
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/config/tools/data_template_codegen.py
@@ -0,0 +1,453 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import json
+import sys
+import os
+import argparse
+import glob
+
+from sys import version_info
+
+if version_info.major == 3:
+ import importlib
+ importlib.reload(sys)
+elif version_info.major == 2:
+ reload(sys)
+ sys.setdefaultencoding("utf-8")
+
+try:
+ import simplejson as json
+except:
+ import json
+
+# {"version":"1.0","properties":[{"id":"light_switch","name":"电灯开关","desc":"控制电灯开灭","required":true,"mode":"rw","define":{"type":"bool","mapping":{"0":"关","1":"开"}}},{"id":"color","name":"颜色","desc":"灯光颜色","mode":"rw","define":{"type":"enum","mapping":{"0":"Red","1":"Green","2":"Blue"}}},{"id":"brightness","name":"颜色","desc":"灯光颜色","mode":"rw","define":{"type":"int","unit":"%","unitDesc":"亮度百分比","min":"0","max":"100"}},{"id":"name","name":"灯位置名称","desc":"灯位置名称:书房、客厅等","mode":"rw","required":true,"define":{"type":"string","min":"0","max":"64"}}]}
+
+class iot_property:
+ """Parse iot property json node and get property code.
+
+ Retrieves rows pertaining to the given keys from the Table instance
+ represented by table_handle. String keys will be UTF-8 encoded.
+
+ Args:
+ json_node:
+ json node include property
+ prefix:
+ property, action, event.
+ Raises:
+ ValueError: invalid node
+ """
+ def __init__(self, json_node, prefix="property", node_define="define"):
+ self.prefix = prefix
+ self.id = json_node["id"]
+ self.type = json_node[node_define]["type"]
+ self.default_value = ""
+ self.struct_property_list = []
+ self.mode = 1 # rw mode
+ try:
+ # property type dict
+ PROPERTY_TYPE_DICT = {
+ # key type_id type_value_name
+ 'bool' : ["DATA_TEMPLATE_TYPE_BOOL" , "value_bool" ],
+ 'enum' : ["DATA_TEMPLATE_TYPE_ENUM" , "value_enum" ],
+ 'float' : ["DATA_TEMPLATE_TYPE_FLOAT" , "value_float" ],
+ 'int' : ["DATA_TEMPLATE_TYPE_INT" , "value_int" ],
+ 'string' : ["DATA_TEMPLATE_TYPE_STRING" , "value_string" ],
+ 'timestamp' : ["DATA_TEMPLATE_TYPE_TIME" , "value_time" ],
+ 'struct' : ["DATA_TEMPLATE_TYPE_STRUCT" , "value_struct" ],
+ 'stringenum': ["DATA_TEMPLATE_TYPE_STRING_ENUM" , "value_string_enum" ],
+ #'array' : ["DATA_TEMPLATE_TYPE_ARRAY" , "value_array" ], # TODO
+ }
+ self.type_id = PROPERTY_TYPE_DICT[self.type][0]
+ self.type_value_name = PROPERTY_TYPE_DICT[self.type][1]
+ # mode: rw or r
+ if "mode" in json_node:
+ self.mode = 1 if json_node["mode"] == "rw" else 0
+ # default value
+ if "mapping" in json_node[node_define]:
+ self.default_value = next(iter(json_node[node_define]["mapping"]))
+ elif "start" in json_node[node_define]:
+ self.default_value = json_node[node_define]["start"]
+ if self.type_id in ["DATA_TEMPLATE_TYPE_STRUCT"]:
+ self.default_value = self.get_property_global_var_name()
+ # string
+ if self.type_id is "DATA_TEMPLATE_TYPE_STRING":
+ self.max_length = json_node[node_define]["max"]
+ self.default_value = "\" \""
+ if self.type_id is "DATA_TEMPLATE_TYPE_STRING_ENUM":
+ self.max_length = 0
+ for enum in json_node[node_define]["mapping"]:
+ self.max_length = max(self.max_length, len(enum))
+ self.default_value = "\"{}\"".format(next(iter(json_node[node_define]["mapping"])))
+
+ # struct
+ if "specs" in json_node[node_define]:
+ for subnode in json_node[node_define]["specs"]:
+ self.struct_property_list.append(iot_property(subnode, self.prefix + '_' + self.id, "dataType"))
+ except KeyError:
+ ValueError('{} 字段 数据类型 type={} 取值非法,有效值应为:bool,enum,float,int,,string,timestamp,struct,stringenum'.format(
+ self.id, self.type))
+
+ def get_property_enum_index(self):
+ return "USR_{}_INDEX_{}".format(self.prefix.upper(), self.id.upper())
+
+ def get_property_global_var_name(self):
+ return "sg_usr_{}_{}".format( self.prefix, self.id)
+
+ def create_property_struct_index_enum(self):
+ main_code = ""
+ main_code += "/*----------------- {} {} index enum start -------------------*/\n\n".format(self.id, self.prefix)
+ main_code += "typedef enum {\n"
+ # 构造枚举索引
+ for property in self.struct_property_list:
+ if self.struct_property_list.index(property) is 0:
+ main_code += " {} = 0,\n".format(property.get_property_enum_index())
+ else:
+ main_code += " {},\n".format(property.get_property_enum_index())
+
+ main_code += "} "
+ main_code += "Usr{}{}Index;\n\n".format(self.prefix.capitalize(), self.id.capitalize())
+ main_code += "/*----------------- {} {} index enum end -------------------*/\n\n".format(self.id, self.prefix)
+ return main_code
+
+ def create_property_struct_definition(self):
+ main_code = ""
+ if self.type_id != "DATA_TEMPLATE_TYPE_STRUCT":
+ return main_code
+ # 定义结构体成员数目
+ main_code += "#define TOTAL_USR_{}_STRUCT_{}_COUNT {}\n\n".format(self.prefix.upper(), self.id.upper(), len(self.struct_property_list))
+ # 定义数据模版结构体
+ main_code += "static DataTemplateProperty {}[TOTAL_USR_{}_STRUCT_{}_COUNT];\n\n".format(self.get_property_global_var_name(), self.prefix.upper(), self.id.upper())
+ # 定义结构体类型和结构体变量
+ main_code += "static void _init_data_template_{}_{}(void)\n".format(self.prefix, self.id)
+ main_code += "{\n"
+ # 构造结构体数组
+ for property in self.struct_property_list:
+ static_property_struct_array_index="{}[{}]".format(self.get_property_global_var_name(), property.get_property_enum_index())
+ if property.type_id in ["DATA_TEMPLATE_TYPE_STRING", "DATA_TEMPLATE_TYPE_STRING_ENUM"]:
+ main_code += " static char {}[{}+1] = {};\n".format(property.get_property_global_var_name(), property.max_length, property.default_value)
+ main_code += " {}[{}].value.{} = {};\n".format(static_property_struct_array_index, property.type_value_name, property.get_property_global_var_name())
+ else:
+ main_code += " {}.value.{} = {};\n".format(static_property_struct_array_index, property.type_value_name, property.default_value)
+ main_code += " {}.key = \"{}\";\n".format(static_property_struct_array_index, property.id)
+ main_code += " {}.type = {};\n".format(static_property_struct_array_index, property.type_id)
+ main_code += " {}.is_rw = {};\n\n".format(static_property_struct_array_index, self.mode)
+ # 结尾
+ main_code += "}\n\n"
+ return main_code
+
+class iot_event:
+ def __init__(self, json_node):
+ self.event_id = json_node["id"]
+ self.event_type = json_node["type"]
+ self.event_type_enum= "IOT_DATA_TEMPLATE_EVENT_TYPE_{}".format(self.event_type.upper())
+ self.event_properties = []
+
+ for property in json_node["params"]:
+ self.event_properties.append(iot_property(property,"event"))
+
+ def get_event_enum_index(self):
+ return "USR_EVENT_INDEX_{}".format(self.event_id.upper())
+
+ def create_event_params_global_var(self):
+ main_code = ""
+ main_code += "/**\n * @brief Sample of event {} post params.\n *\n */\n".format(self.event_id)
+ main_code += "static const char* sg_usr_event_{}_params = ".format(self.event_id)
+ main_code += "\"{"
+
+ for property in self.event_properties:
+ if self.event_properties.index(property) > 0:
+ main_code += ","
+ if property.type_id in ["DATA_TEMPLATE_TYPE_STRING", "DATA_TEMPLATE_TYPE_STRING_ENUM"]:
+ main_code += "\\\"{}\\\":{}".format(property.id, property.default_value.replace('\"','\\\"'))
+ else:
+ main_code += "\\\"{}\\\":{}".format(property.id, property.default_value)
+
+ main_code += "}\";\n\n"
+ return main_code
+
+class iot_action:
+ def __init__(self, json_node):
+ self.action_id = json_node["id"]
+
+ self.action_input = []
+ for input in json_node["input"]:
+ self.action_input.append(iot_property(input, 'action_' + self.action_id + '_input'))
+
+ self.action_output = []
+ for output in json_node["output"]:
+ self.action_output.append(iot_property(output, 'action_' + self.action_id + '_output'))
+
+ def get_action_enum_index(self):
+ return "USR_ACTION_INDEX_{}".format(self.action_id.upper())
+
+ def get_action_input_index_enum(self) :
+ main_code = ""
+ main_code += "/*----------------- action {} input index enum start -------------------*/\n\n".format(self.action_id)
+ main_code += "typedef enum {\n"
+
+ for property in self.action_input:
+ if self.action_input.index(property) is 0:
+ main_code += " {} = 0,\n".format(property.get_property_enum_index())
+ else:
+ main_code += " {},\n".format(property.get_property_enum_index())
+ main_code += "} "
+ main_code += "UsrAction{}InputIndex;\n\n".format(self.action_id.capitalize().replace('_', ''))
+ main_code += "/*----------------- action {} input index enum end -------------------*/\n\n".format(self.action_id)
+ return main_code
+
+ def create_action_input_init(self):
+ static_usr_action_input_array_name = "sg_usr_action_{}_input".format(self.action_id)
+
+ main_code = ""
+ main_code += "#define TOTAL_USR_ACTION_{}_INPUT_PARAMS_COUNT {}\n\n".format(self.action_id.upper(), len(self.action_input))
+ main_code += "static DataTemplateProperty {}[TOTAL_USR_ACTION_{}_INPUT_PARAMS_COUNT];\n\n".format(static_usr_action_input_array_name, self.action_id.upper())
+ main_code += "static void _init_data_template_action_{}_input(void)\n".format(self.action_id)
+ main_code += "{\n"
+
+ input_struct = ""
+ for property in self.action_input:
+ if property.type_id is "DATA_TEMPLATE_TYPE_STRUCT":
+ input_struct += property.create_property_struct_definition()
+ if property.type_id in ["DATA_TEMPLATE_TYPE_STRING", "DATA_TEMPLATE_TYPE_STRING_ENUM"]:
+ main_code += " static char {}[{}+1]= {};\n".format(property.get_property_global_var_name(), property.max_length, property.default_value)
+ array_index = "{}[{}]".format(static_usr_action_input_array_name, property.get_property_enum_index())
+ main_code += " {}.value.{} = {};\n".format(array_index, property.type_value_name, property.default_value)
+ main_code += " {}.key = \"{}\";\n".format(array_index, property.id)
+ main_code += " {}.type = {};\n".format(array_index, property.type_id)
+ main_code += " {}.is_rw = {};\n\n".format(array_index, property.mode)
+ main_code += "}\n\n"
+ return main_code
+
+ def create_action_reply(self):
+ main_code = ""
+ input_struct = ""
+
+ main_code += "/**\n * @brief Sample of action {} reply.\n *\n */\n".format(self.action_id)
+ main_code += "static IotDataTemplateActionReply sg_usr_action_{}_reply = ".format(self.action_id)
+ main_code += "{\n"
+ main_code += " .code = 0,\n"
+ main_code += " .client_token = {"
+ main_code += ".value = \"test_{}\", .value_len = sizeof(\"test_{}\")".format(self.action_id, self.action_id)
+ main_code += "},\n"
+ main_code += " .response = \"{"
+
+ for property in self.action_output:
+ if self.action_output.index(property) > 0:
+ main_code += ","
+ if property.type_id == "DATA_TEMPLATE_TYPE_STRUCT":
+ input_struct += property.create_property_struct_definition()
+ main_code += "\\\"{}\\\":{}".format(property.id, property.default_value)
+ main_code += "}\",\n};\n\n"
+ # print(main_code)
+ return main_code
+
+class iot_struct:
+ def __init__(self, model):
+ self.properties = []
+ self.events = []
+ self.actions = []
+
+ if "properties" in model:
+ for property in model["properties"]:
+ self.properties.append(iot_property(property))
+
+ if "events" in model:
+ for event in model["events"]:
+ self.events.append(iot_event(event))
+
+ if "actions" in model:
+ for action in model["actions"]:
+ self.actions.append(iot_action(action))
+
+ def __index_enum_create(self, list, prefix) :
+ property_struct_index_enum_str = ""
+ action_input_index_enum_str = ""
+
+ main_code = ""
+ main_code += "/*----------------- {} index enum start -------------------*/\n\n".format(prefix)
+ main_code += "typedef enum {\n"
+
+ enum_index = ""
+ for node in list:
+ if prefix is "property":
+ enum_index = node.get_property_enum_index()
+ if node.type_id is "DATA_TEMPLATE_TYPE_STRUCT":
+ property_struct_index_enum_str += node.create_property_struct_index_enum()
+ elif prefix is "action":
+ enum_index = node.get_action_enum_index()
+ action_input_index_enum_str += node.get_action_input_index_enum()
+ elif prefix is "event":
+ enum_index = node.get_event_enum_index()
+
+ if list.index(node) is 0:
+ main_code += " {} = 0,\n".format(enum_index)
+ else:
+ main_code += " {},\n".format(enum_index)
+
+ main_code += "} "
+ main_code += "Usr{}Index;\n\n".format(prefix.capitalize())
+ main_code += "/*----------------- {} index enum end -------------------*/\n\n".format(prefix)
+
+ main_code += property_struct_index_enum_str
+ main_code += action_input_index_enum_str
+ return main_code
+
+ def __property_data_initializer(self) :
+ main_code = ""
+ main_code += "// ----------------------------------------------------------------------------\n"
+ main_code += "// user property\n"
+ main_code += "// ----------------------------------------------------------------------------/\n\n"
+
+ property_code = ""
+ property_code += "#define TOTAL_USR_PROPERTY_COUNT {}\n\n".format(len(self.properties))
+ property_code += "static DataTemplateProperty sg_usr_data_template_property[TOTAL_USR_PROPERTY_COUNT];\n\n"
+ property_code += "static void _init_data_template_property(void)\n"
+ property_code += "{\n"
+ for property in self.properties:
+ if property.type_id is "DATA_TEMPLATE_TYPE_STRUCT":
+ main_code += property.create_property_struct_definition()
+ property_code += " _init_data_template_property_{}();\n".format(property.id)
+
+ if property.type_id in ["DATA_TEMPLATE_TYPE_STRING", "DATA_TEMPLATE_TYPE_STRING_ENUM"]:
+ property_code += " static char {}[{}+1] = {};\n".format(property.get_property_global_var_name(), property.max_length, property.default_value)
+
+ static_usr_property_array_name = "sg_usr_data_template_property[{}]".format(property.get_property_enum_index())
+
+ if property.type_id is "DATA_TEMPLATE_TYPE_STRUCT":
+ property_code += " {}.value.{}.property = {};\n".format(static_usr_property_array_name, property.type_value_name, property.default_value)
+ property_code += " {}.value.{}.count = TOTAL_USR_PROPERTY_STRUCT_{}_COUNT;\n".format(static_usr_property_array_name, property.type_value_name, property.id.upper())
+ elif property.type_id in ["DATA_TEMPLATE_TYPE_STRING", "DATA_TEMPLATE_TYPE_STRING_ENUM"]:
+ property_code+= " {}.value.{} = {};\n".format(static_usr_property_array_name, property.type_value_name, property.get_property_global_var_name())
+ else:
+ property_code += " {}.value.{} = {};\n".format(static_usr_property_array_name, property.type_value_name, property.default_value)
+ property_code += " {}.key = \"{}\";\n".format(static_usr_property_array_name, property.id)
+ property_code += " {}.type = {};\n".format(static_usr_property_array_name, property.type_id)
+ property_code += " {}.need_report = {};\n".format(static_usr_property_array_name, 1)
+ property_code += " {}.is_rw = {};\n\n".format(static_usr_property_array_name, property.mode)
+ property_code += "}\n\n"
+
+ main_code += property_code
+ return main_code
+
+ def __event_data_initializer(self) :
+ main_code = ""
+ main_code += "// ----------------------------------------------------------------------------\n"
+ main_code += "// user event\n"
+ main_code += "// ----------------------------------------------------------------------------\n\n"
+
+ event_code = ""
+ event_code += "#define TOTAL_USR_EVENT_COUNT {}\n\n".format(len(self.events))
+ event_code += "static DataTemplateEvent sg_usr_data_template_event[TOTAL_USR_EVENT_COUNT];\n\n"
+ event_code += "static void _init_data_template_event(void)\n"
+ event_code += "{\n"
+
+ for event in self.events:
+ main_code += event.create_event_params_global_var()
+ static_usr_event_array_name = "sg_usr_data_template_event[{}]".format(event.get_event_enum_index())
+ event_code += " {}.event_id = \"{}\";\n".format(static_usr_event_array_name, event.event_id)
+ event_code += " {}.type = {};\n".format(static_usr_event_array_name, event.event_type_enum)
+ event_code += " {}.params = sg_usr_event_{}_params;\n\n".format(static_usr_event_array_name, event.event_id)
+ event_code += "}\n\n"
+
+ main_code += event_code
+ # print(event_params)
+ return main_code
+
+ def __action_data_initializer(self):
+ main_code = ""
+
+ action_code = ""
+ main_code += "// ----------------------------------------------------------------------------\n"
+ main_code += "// user action\n"
+ main_code += "// ----------------------------------------------------------------------------\n\n"
+
+ action_code += "#define TOTAL_USR_ACTION_COUNT {}\n\n".format(len(self.actions))
+ action_code += "static DataTemplateAction sg_usr_data_template_action[TOTAL_USR_ACTION_COUNT];\n\n"
+ action_code += "static void _init_data_template_action(void)\n"
+ action_code += "{\n"
+
+ for action in self.actions:
+ main_code += action.create_action_input_init()
+ main_code += action.create_action_reply()
+
+ static_usr_action_input_array_name = "sg_usr_data_template_action[{}]".format(action.get_action_enum_index())
+ action_code += " _init_data_template_action_{}_input();\n".format(action.action_id)
+ action_code += " {}.action_id = \"{}\";\n".format(static_usr_action_input_array_name, action.action_id)
+ action_code += " {}.input_struct.value_struct.property = sg_usr_action_{}_input;\n".format(static_usr_action_input_array_name,action.action_id)
+ action_code += " {}.input_struct.value_struct.count = TOTAL_USR_ACTION_{}_INPUT_PARAMS_COUNT;\n".format(static_usr_action_input_array_name,action.action_id.upper())
+ action_code += " {}.reply = sg_usr_action_{}_reply;\n\n".format(static_usr_action_input_array_name, action.action_id)
+ action_code += "}\n\n"
+
+ main_code += action_code
+ return main_code
+
+ def gen_config_header(self):
+ main_code = ""
+ main_code += self.__index_enum_create(self.properties, "property")
+ main_code += self.__index_enum_create(self.events, "event")
+ main_code += self.__index_enum_create(self.actions, "action")
+ return main_code
+
+ def gen_config_src_c(self):
+ main_code = ""
+ main_code += self.__property_data_initializer()
+ main_code += self.__event_data_initializer()
+ main_code += self.__action_data_initializer()
+ return main_code
+
+def main():
+ parser = argparse.ArgumentParser(description='Iot hub data_template and events config code generator.',
+ usage='use "./data_template_codegen.py -c xx/config.json" gen config code')
+ parser.add_argument('-c', '--config', dest='config', metavar='xxx.json', required=False, default='xxx.json',
+ help='copy the generated file (data_config.c and events_config.c) to data_template_sample dir '
+ 'or your own code dir with data_template. '
+ '\nconfig file can be download from tencent iot-hub platfrom. https://console.cloud.tencent.com/iotcloud')
+ parser.add_argument('-d', '--dest', dest='dest', required=False, default='.',
+ help='Dest directory for generated code files, no / at the end.')
+ args = parser.parse_args()
+
+ config_path = args.config
+ if not os.path.exists(config_path):
+ print(u"错误:配置文件不存在,请重新指定数据模板配置文件路径,请参考用法 ./data_template_code_generate.py -c /data_template.json".format(config_path))
+ return 1
+
+ config_dir = os.path.dirname(config_path)
+ if config_dir:
+ config_dir += "/"
+
+ f = open(config_path, "r", encoding='utf-8')
+ try:
+ thing_model = json.load(f)
+ if 'properties' not in thing_model:
+ thing_model.properties = []
+
+ if 'events' not in thing_model:
+ thing_model.events = []
+
+ print(u"加载 {} 文件成功".format(config_path))
+ except ValueError as e:
+ print(u"错误:文件格式非法,请检查 {} 文件是否是 JSON 格式。".format(config_path))
+ return 1
+
+ try:
+ snippet = iot_struct(thing_model)
+
+ output_config_header_file_name = args.dest + "/data_template_config_header.include"
+ with open(output_config_header_file_name, "w") as file:
+ file.write("{}".format(snippet.gen_config_header()))
+ file.close()
+ print(u"文件 {} 生成成功".format(output_config_header_file_name))
+
+ output_config_src_c_file_name = args.dest + "/data_template_config_src_c.include"
+ with open(output_config_src_c_file_name, "w") as file:
+ file.write("{}".format(snippet.gen_config_src_c()))
+ file.close()
+ print(u"文件 {} 生成成功".format(output_config_src_c_file_name))
+ return 0
+ except ValueError as e:
+ print(e)
+ return 1
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/components/connectivity/iot-hub-device-c-sdk/config/tools/example_config.json b/components/connectivity/iot-hub-device-c-sdk/config/tools/example_config.json
new file mode 100644
index 00000000..e89d5b11
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/config/tools/example_config.json
@@ -0,0 +1,258 @@
+{
+ "version": "1.0",
+ "properties": [
+ {
+ "id": "power_switch",
+ "name": "电灯开关",
+ "desc": "控制电灯开灭",
+ "required": true,
+ "mode": "rw",
+ "define": {
+ "type": "bool",
+ "mapping": {
+ "0": "关",
+ "1": "开"
+ }
+ }
+ },
+ {
+ "id": "color",
+ "name": "颜色",
+ "desc": "灯光颜色",
+ "mode": "rw",
+ "define": {
+ "type": "enum",
+ "mapping": {
+ "0": "Red",
+ "1": "Green",
+ "2": "Blue"
+ }
+ }
+ },
+ {
+ "id": "brightness",
+ "name": "亮度",
+ "desc": "灯光亮度",
+ "mode": "rw",
+ "define": {
+ "type": "int",
+ "unit": "%",
+ "step": "1",
+ "min": "0",
+ "max": "100",
+ "start": "1"
+ }
+ },
+ {
+ "id": "name",
+ "name": "灯位置名称",
+ "desc": "灯位置名称:书房、客厅等",
+ "mode": "rw",
+ "required": false,
+ "define": {
+ "type": "string",
+ "min": "0",
+ "max": "64"
+ }
+ },
+ {
+ "id": "position",
+ "name": "灯位置坐标",
+ "desc": "",
+ "mode": "r",
+ "define": {
+ "type": "struct",
+ "specs": [
+ {
+ "id": "longitude",
+ "name": "经度",
+ "dataType": {
+ "type": "int",
+ "min": "-180",
+ "max": "180",
+ "start": "1",
+ "step": "1",
+ "unit": "度"
+ }
+ },
+ {
+ "id": "latitude",
+ "name": "纬度",
+ "dataType": {
+ "type": "int",
+ "min": "-90",
+ "max": "90",
+ "start": "1",
+ "step": "1",
+ "unit": "度"
+ }
+ }
+ ]
+ },
+ "required": false
+ },
+ {
+ "id": "power",
+ "name": "功率",
+ "desc": "灯泡功率",
+ "mode": "rw",
+ "define": {
+ "type": "stringenum",
+ "mapping": {
+ "high": "High",
+ "medium": "Medium",
+ "low": "Low"
+ }
+ }
+ }
+ ],
+ "events": [
+ {
+ "id": "status_report",
+ "name": "DeviceStatus",
+ "desc": "Report the device status",
+ "type": "info",
+ "required": false,
+ "params": [
+ {
+ "id": "status",
+ "name": "running_state",
+ "desc": "Report current device running state",
+ "define": {
+ "type": "bool",
+ "mapping": {
+ "0": "normal",
+ "1": "fault"
+ }
+ }
+ },
+ {
+ "id": "message",
+ "name": "Message",
+ "desc": "Some extra message",
+ "define": {
+ "type": "string",
+ "min": "0",
+ "max": "64"
+ }
+ }
+ ]
+ },
+ {
+ "id": "low_voltage",
+ "name": "LowVoltage",
+ "desc": "Alert for device voltage is low",
+ "type": "alert",
+ "required": false,
+ "params": [
+ {
+ "id": "voltage",
+ "name": "Voltage",
+ "desc": "Current voltage",
+ "define": {
+ "type": "float",
+ "unit": "V",
+ "step": "1",
+ "min": "0.0",
+ "max": "24.0",
+ "start": "1"
+ }
+ }
+ ]
+ },
+ {
+ "id": "hardware_fault",
+ "name": "Hardware_fault",
+ "desc": "Report hardware fault",
+ "type": "fault",
+ "required": false,
+ "params": [
+ {
+ "id": "name",
+ "name": "Name",
+ "desc": "Name like: memory,tf card, censors ...",
+ "define": {
+ "type": "string",
+ "min": "0",
+ "max": "64"
+ }
+ },
+ {
+ "id": "error_code",
+ "name": "Error_Code",
+ "desc": "Error code for fault",
+ "define": {
+ "type": "int",
+ "unit": "",
+ "step": "1",
+ "min": "0",
+ "max": "2000",
+ "start": "1"
+ }
+ }
+ ]
+ }
+ ],
+ "actions": [
+ {
+ "id": "light_blink",
+ "name": "light_blink",
+ "desc": "根据time和color实现灯的闪烁",
+ "input": [
+ {
+ "id": "time",
+ "name": "time",
+ "define": {
+ "type": "int",
+ "min": "0",
+ "max": "10",
+ "start": "0",
+ "step": "1",
+ "unit": "秒"
+ }
+ },
+ {
+ "id": "color",
+ "name": "color",
+ "define": {
+ "type": "bool",
+ "mapping": {
+ "0": "red",
+ "1": "green"
+ }
+ }
+ },
+ {
+ "id": "total_time",
+ "name": "total_time",
+ "define": {
+ "type": "int",
+ "min": "0",
+ "max": "100",
+ "start": "0",
+ "step": "1",
+ "unit": "秒"
+ }
+ }
+ ],
+ "output": [
+ {
+ "id": "err_code",
+ "name": "code",
+ "define": {
+ "type": "enum",
+ "mapping": {
+ "0": "ok",
+ "1": "failed"
+ }
+ }
+ }
+ ],
+ "required": false
+ }
+ ],
+ "profile": {
+ "ProductId": "Q5NNWVC2Z8",
+ "CategoryId": "3"
+ }
+}
\ No newline at end of file
diff --git a/components/connectivity/iot-hub-device-c-sdk/docs/SDK代码格式说明.md b/components/connectivity/iot-hub-device-c-sdk/docs/SDK代码格式说明.md
new file mode 100644
index 00000000..15caf3b9
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/docs/SDK代码格式说明.md
@@ -0,0 +1,148 @@
+
+
+- [一、编写目的](#%E4%B8%80%E7%BC%96%E5%86%99%E7%9B%AE%E7%9A%84)
+- [二、代码格式](#%E4%BA%8C%E4%BB%A3%E7%A0%81%E6%A0%BC%E5%BC%8F)
+ - [行长度](#%E8%A1%8C%E9%95%BF%E5%BA%A6)
+ - [缩进](#%E7%BC%A9%E8%BF%9B)
+ - [文件编码](#%E6%96%87%E4%BB%B6%E7%BC%96%E7%A0%81)
+ - [分行](#%E5%88%86%E8%A1%8C)
+ - [条件语句](#%E6%9D%A1%E4%BB%B6%E8%AF%AD%E5%8F%A5)
+ - [循环和开关语句](#%E5%BE%AA%E7%8E%AF%E5%92%8C%E5%BC%80%E5%85%B3%E8%AF%AD%E5%8F%A5)
+ - [指针](#%E6%8C%87%E9%92%88)
+ - [水平留白](#%E6%B0%B4%E5%B9%B3%E7%95%99%E7%99%BD)
+ - [垂直留白](#%E5%9E%82%E7%9B%B4%E7%95%99%E7%99%BD)
+
+
+## 一、编写目的
+
+该文档用作提供 SDK 开发的代码格式说明,开发者提交代码前应仔细阅读。
+
+本项目根目录提供了`.clang-format`格式文件,请优先使用`clang-format`进行代码格式统一。
+
+如有冲突,请以`clang-format`的结果为准。
+
+## 二、代码格式
+
+### 行长度
+
+最长120行
+
+### 缩进
+
+四个空格
+
+### 文件编码
+
+UTF-8 不能有BOM头。
+
+### 分行
+
+1.对齐式
+
+```c
+bool retval = DoSomething(averyveryveryverylongargument1,
+ argument2, argument3);
+```
+
+2.缩进式
+
+```c
+if (...) {
+ ...
+ ...
+ if (...) {
+ DoSomething(
+ argument1, argument2, // 4 空格缩进
+ argument3, argument4);
+ }
+```
+
+### 条件语句
+
+```c
+if (condition) {
+ DoSomething(); // 4 空格缩进.
+}
+```
+
+### 循环和开关语句
+
+- default不可达时,用assert
+- case使用大括号非强制要求
+
+```c
+switch (var) {
+ case 0: { // 4 空格缩进
+ ... // 4 空格缩进
+ break;
+ }
+ case 1: {
+ ...
+ break;
+ }
+ default: {
+ assert(false);
+ }
+}
+```
+
+### 指针
+
+```
+char* ptr = NULL;
+```
+
+### 水平留白
+
+行尾不留白
+
+```c
+void f(bool b) { // 左大括号前总是有空格
+ ...
+int i = 0; // 分号前不加空格
+
+// 列表初始化中大括号内的空格是可选的,
+// 如果加了空格,那么两边都要加上
+int x[] = { 0 };
+int x[] = {0};
+
+if (b) { // if 条件语句和循环语句关键字后均有空格
+} else { // else 前后有空格
+}
+
+while (test) {} // 圆括号内部不紧邻空格
+switch (i) {
+for (int i = 0; i < 5; ++i) {
+
+// 循环和条件语句的圆括号内可以有空格,
+// 但这种情况很少,要保持一致
+switch ( i ) {
+if ( test ) {
+for ( int i = 0; i < 5; ++i ) {
+
+// 循环里内分号后恒有空格,分号前可以加个空格
+for ( ; i < 5 ; ++i) {
+switch (i) {
+ case 1: // switch case 的冒号前无空格
+ ...
+ case 2: break; // 如果冒号后有代码,加个空格
+
+// 赋值运算符前后总是有空格
+x = 0;
+
+// 其它二元操作符前后也恒有空格,对于表达式的子式可以不加空格
+// 圆括号内部没有紧邻空格
+v = w * x + y / z;
+v = w*x + y/z;
+v = w * (x + z);
+
+// 在参数和一元操作符之间不加空格
+x = -5;
+++x;
+if (x && !y)
+ ...
+```
+
+### 垂直留白
+
+看情况,根据个人习惯,增强可读性即可
diff --git a/components/connectivity/iot-hub-device-c-sdk/docs/SDK代码规范说明.md b/components/connectivity/iot-hub-device-c-sdk/docs/SDK代码规范说明.md
new file mode 100644
index 00000000..3d4383b1
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/docs/SDK代码规范说明.md
@@ -0,0 +1,528 @@
+
+
+- [编写目的](#%E7%BC%96%E5%86%99%E7%9B%AE%E7%9A%84)
+- [代码规范描述](#%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83%E6%8F%8F%E8%BF%B0)
+ - [头文件](#%E5%A4%B4%E6%96%87%E4%BB%B6)
+ - [头文件保护](#%E5%A4%B4%E6%96%87%E4%BB%B6%E4%BF%9D%E6%8A%A4)
+ - [内联及结构体](#%E5%86%85%E8%81%94%E5%8F%8A%E7%BB%93%E6%9E%84%E4%BD%93)
+ - [前置声明](#%E5%89%8D%E7%BD%AE%E5%A3%B0%E6%98%8E)
+ - [内联函数](#%E5%86%85%E8%81%94%E5%87%BD%E6%95%B0)
+ - [头文件包含顺序](#%E5%A4%B4%E6%96%87%E4%BB%B6%E5%8C%85%E5%90%AB%E9%A1%BA%E5%BA%8F)
+ - [作用域](#%E4%BD%9C%E7%94%A8%E5%9F%9F)
+ - [静态变量](#%E9%9D%99%E6%80%81%E5%8F%98%E9%87%8F)
+ - [局部变量](#%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F)
+ - [函数](#%E5%87%BD%E6%95%B0)
+ - [输出参数](#%E8%BE%93%E5%87%BA%E5%8F%82%E6%95%B0)
+ - [简短函数](#%E7%AE%80%E7%9F%AD%E5%87%BD%E6%95%B0)
+ - [语言特性](#%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7)
+ - [const](#const)
+ - [整形](#%E6%95%B4%E5%BD%A2)
+ - [位可移植性](#%E4%BD%8D%E5%8F%AF%E7%A7%BB%E6%A4%8D%E6%80%A7)
+ - [预处理宏](#%E9%A2%84%E5%A4%84%E7%90%86%E5%AE%8F)
+ - [NULL和0](#null%E5%92%8C0)
+ - [sizeof](#sizeof)
+ - [别名](#%E5%88%AB%E5%90%8D)
+ - [动态分配](#%E5%8A%A8%E6%80%81%E5%88%86%E9%85%8D)
+ - [命名约定](#%E5%91%BD%E5%90%8D%E7%BA%A6%E5%AE%9A)
+ - [通用命名规则](#%E9%80%9A%E7%94%A8%E5%91%BD%E5%90%8D%E8%A7%84%E5%88%99)
+ - [文件命名](#%E6%96%87%E4%BB%B6%E5%91%BD%E5%90%8D)
+ - [类型命名](#%E7%B1%BB%E5%9E%8B%E5%91%BD%E5%90%8D)
+ - [变量命名](#%E5%8F%98%E9%87%8F%E5%91%BD%E5%90%8D)
+ - [常量命名、枚举型变量以及宏](#%E5%B8%B8%E9%87%8F%E5%91%BD%E5%90%8D%E6%9E%9A%E4%B8%BE%E5%9E%8B%E5%8F%98%E9%87%8F%E4%BB%A5%E5%8F%8A%E5%AE%8F)
+ - [函数命名](#%E5%87%BD%E6%95%B0%E5%91%BD%E5%90%8D)
+ - [注释](#%E6%B3%A8%E9%87%8A)
+ - [注释风格](#%E6%B3%A8%E9%87%8A%E9%A3%8E%E6%A0%BC)
+ - [注释示例](#%E6%B3%A8%E9%87%8A%E7%A4%BA%E4%BE%8B)
+ - [格式](#%E6%A0%BC%E5%BC%8F)
+ - [行长度](#%E8%A1%8C%E9%95%BF%E5%BA%A6)
+ - [非ASCII字符](#%E9%9D%9Eascii%E5%AD%97%E7%AC%A6)
+ - [空格](#%E7%A9%BA%E6%A0%BC)
+ - [函数声明与定义](#%E5%87%BD%E6%95%B0%E5%A3%B0%E6%98%8E%E4%B8%8E%E5%AE%9A%E4%B9%89)
+ - [浮点字面量](#%E6%B5%AE%E7%82%B9%E5%AD%97%E9%9D%A2%E9%87%8F)
+ - [函数调用](#%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8)
+ - [列表初始化](#%E5%88%97%E8%A1%A8%E5%88%9D%E5%A7%8B%E5%8C%96)
+ - [条件语句](#%E6%9D%A1%E4%BB%B6%E8%AF%AD%E5%8F%A5)
+ - [循环和开关选择语句](#%E5%BE%AA%E7%8E%AF%E5%92%8C%E5%BC%80%E5%85%B3%E9%80%89%E6%8B%A9%E8%AF%AD%E5%8F%A5)
+ - [指针和引用表达式](#%E6%8C%87%E9%92%88%E5%92%8C%E5%BC%95%E7%94%A8%E8%A1%A8%E8%BE%BE%E5%BC%8F)
+ - [布尔表达式](#%E5%B8%83%E5%B0%94%E8%A1%A8%E8%BE%BE%E5%BC%8F)
+ - [返回值](#%E8%BF%94%E5%9B%9E%E5%80%BC)
+ - [变量及数组初始化](#%E5%8F%98%E9%87%8F%E5%8F%8A%E6%95%B0%E7%BB%84%E5%88%9D%E5%A7%8B%E5%8C%96)
+ - [预处理指令](#%E9%A2%84%E5%A4%84%E7%90%86%E6%8C%87%E4%BB%A4)
+ - [水平留白](#%E6%B0%B4%E5%B9%B3%E7%95%99%E7%99%BD)
+ - [垂直留白](#%E5%9E%82%E7%9B%B4%E7%95%99%E7%99%BD)
+
+
+## 编写目的
+
+该文档用作提供 SDK 开发的代码规范说明,开发者提交代码前应仔细阅读。
+
+## 代码规范描述
+
+### 头文件
+
+#### 头文件保护
+
+头文件应支持c++引用,并提供保护头,根据开源治理要求,本项目中保护头定义如下:
+
+`IOT_HUB_DEVICE_C_SDK__INC__`
+
+比如 `mqtt_client.h`:
+
+```c
+#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_MQTT_CLIENT_INC_MQTT_CLIENT_H_
+#define IOT_HUB_DEVICE_C_SDK_SERVICES_MQTT_CLIENT_INC_MQTT_CLIENT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_MQTT_CLIENT_INC_MQTT_CLIENT_H_
+```
+
+#### 内联及结构体
+
+头文件中若声明联函数及结构体,则必须定义,即如果对外提供结构体以及内联函数,请务必放到头文件中定义。
+
+#### 前置声明
+
+尽量避免前置声明,而采用包含头文件。
+
+#### 内联函数
+
+内联函数不超过10行。
+
+C语言使用内联函数需要注意,C99才开始支持,并且一般需要编译器开了优化选项才支持,这个跟C++不一样。
+
+#### 头文件包含顺序
+
+通常系统头文件会放到相关头文件中,以保证相关头文件的易用性。
+
+```c
+dir2/foo2.h(相关头文件)
+空行
+C 系统头文件
+空行
+其他库的 `.h`文件
+本项目内 `.h`文件
+```
+
+---
+
+### 作用域
+
+#### 静态变量
+
+在源文件中定义一个不需要被外部引用的变量时,可以将它们声明为`static`。但是不要在头文件中这么做。
+
+#### 局部变量
+
+将函数变量尽可能置于最小作用域内,并在变量声明时进行初始化。
+
+```c
+int i = 1;
+
+while (const char* p = strchr(str, '/')) str = p + 1;
+```
+
+---
+
+### 函数
+
+#### 输出参数
+
+1. 推荐优先使用返回值作为函数输出。函数的参数列表排序为:输入参数在前,输出参数在后。
+2. 输入参数,通常是值参或者是 const 引用,纯输出参数和输入兼输出参数则是非 const 指针。
+3. 参数排序时,请将所有的纯输入参数置于所有输出参数之前。
+
+以上从易读性角度考虑,不做强制要求,但是在函数的`doxygen`注释中需要通过`@param[in]`、`@param[out]`、`@param[in,out]`列出。
+
+#### 简短函数
+
+如果一个函数超过了 40 行,则可以思考下,能否在不破坏程序结构的前提之下,对函数进行拆分。
+
+以上从易读性角度考虑,当`review`时超过3次无法看懂,即需考虑进行拆分。
+
+---
+
+### 语言特性
+
+#### const
+
+在 API 里,只要合理,就应该使用 const
+
+- 如果函数不会修改传你入的引用或指针类型参数,该参数应声明为 const
+- 按值传入的函数参数,const 对调用者没有影响,因此不建议在函数声明中使用
+- 尽可能将方法声明为 const,除非它们改变了对象的逻辑状态,否则不能安全地并发调用它们。
+
+#### 整形
+
+1. 在 C 整型中, 只使用 `int`。在合适的情况下,推荐使用标准类型如 `size_t` 和 `ptrdiff_t`。大多数情况下,`int`即可覆盖。
+2. ``定义了 `int16_t`、`uint32_t`、`int64_t` 等整型,在需要确保整型大小时可以使用它们代替 short、unsigned long long 等。
+
+#### 位可移植性
+
+1. 请记住,sizeof(void *) != sizeof(int)。如果需要指针大小的整数,请使用 `intptr_t`
+2. 根据需要使用大括号初始化来创建 64 位常量
+
+```c
+int64_t my_value{0x123456789};
+uint64_t my_mask{3ULL << 48};
+```
+
+#### 预处理宏
+
+使用宏时要非常谨慎,尽量以内联函数,枚举和常量代替。往用宏展开性能关键的代码,现在可以用内联函数替代。用宏表示常量可被 const 或constexpr 变量代替。用宏 "缩写" 长变量名可被引用代替。
+下面给出的用法模式可以避免使用宏带来的问题;如果你要用宏,尽可能遵守:
+
+- 不要在 .h 文件中定义宏,常见的通用配置需要在头文件中定义,宏命名应清晰且易区分。
+- 在马上要使用时才进行 #define,使用后要立即 #undef。
+- 不要只是对已经存在的宏使用 #undef,选择一个不会冲突的名称。
+- 不要用 ## 处理函数,类和变量的名字。
+
+#### NULL和0
+
+整数用 0,浮点数用 0.0,指针用 NULL,字符用 '\0'。
+
+#### sizeof
+
+尽可能用 sizeof(varname) 代替 sizeof(type)。
+
+#### 别名
+
+不要把仅用于实现的别名放到公共 API 里;公共 API 里只放用户使用的别名。
+
+在定义公共别名时,把新名字的意图写入文档,包括:是否保证始终和当前指向的类型相同,或是否打算使用更有限的兼容性。这让用户知道:是否可以将这些类型视为可替换类型,或者是否必须遵循更特定的规则,并且帮助实现部分保留别名更改的自由度。
+
+#### 动态分配
+
+如果必须使用动态分配,那么更倾向于将所有权保持在分配者手中。如果其他地方要使用这个对象,最好传递它的拷贝,或者传递一个不用改变所有权的指针或引用。
+
+### 命名约定
+
+#### 通用命名规则
+
+函数命名、变量命名、文件命名要有描述性,少用缩写。
+
+#### 文件命名
+
+文件名要全部小写,可以包含下划线 "`_`" 或连字符 "`-`",依照项目的约定.如果没有约定,那么 "`_`" 更好。
+不要使用已经存在于 /usr/include 下的文件名,如 db.h。
+
+#### 类型命名
+
+结构体、类型定义 (typedef)、枚举——均使用相同约定,即以大写字母开始,每个单词首字母均大写,不包含下划线。
+
+```c
+struct UrlTableProperties {
+ string name;
+ int num_entries;
+};
+// 枚举
+enum UrlTableErrors { ...
+```
+
+#### 变量命名
+
+变量 (包括函数参数) 和数据成员名一律小写,单词之间用下划线连接。其中全局变量中`static`修饰的以`sg`开头,非`static`修饰的以`g`开头
+
+```c
+static int sg_count = 0;
+int g_count = 0;
+```
+
+#### 常量命名、枚举型变量以及宏
+
+声明为const 的变量、枚举型变量或者宏,或在程序运行期间其值始终保持不变的,全部使用大写,单词用下划线`_`隔开。
+
+```c
+const MAX_COUNT = 10;
+#define MAX_SIZE_OF_NUM 10
+enum PinStateType {
+ PIN_OFF,
+ PIN_ON
+};
+```
+
+#### 函数命名
+
+内部函数命名均已小写形式,加下划线隔开
+
+```c
+const char *iot_ca_get(void);
+```
+
+对外API以及跨平台移植接口使用大小写混合,用下划线隔开,并以`IOT`或者`HAL`开头
+
+```c
+void *IOT_OTA_Init(const char *product_id, const char *device_name, void *ch_signal);
+int HAL_AT_Uart_Send(void *data, uint32_t size)
+```
+
+---
+
+### 注释
+
+#### 注释风格
+
+统一使用`doxegen`注释风格。
+
+#### 注释示例
+
+- 文件头:所有文件的开头应该包含以下内容:
+
+```c
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file mqtt_packet_deserialize.c
+ * @brief implements mqtt packet deserialize. Reference paho.mqtt.embedded-c &
+ * http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.pdf
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-21
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-21 1.0 fancyxu first commit
+ *
+ */
+```
+
+- 函数注释:参数的输入/输出必须指明
+
+```c
+/**
+ * @brief Deserialize the supplied (wire) buffer into publish data. See 3.3.
+ *
+ * @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
+ * @param[in] buf_len the length in bytes of the data in the supplied buffer
+ * @param[out] flags the MQTT dup, qos, retained flag
+ * @param[out] packet_id returned integer - the MQTT packet identifier
+ * @param[out] topic_name returned string - the MQTT topic in the publish
+ * @param[out] topic_len returned integer - the length of the MQTT topic
+ * @param[out] payload returned byte buffer - the MQTT publish payload
+ * @param[out] payload_len returned integer - the length of the MQTT payload
+ * @return @see MQTTPacketErrCode
+ */
+```
+
+- 结构体注释:结构体成员必须采用`/**< xxx */`才能显示在`doxygen`中
+
+```c
+/**
+ * @brief Defines the MQTT "Last Will and Testament" (LWT) settings for the connect packet.
+ *
+ */
+typedef struct {
+ const char* topic_name; /**< The LWT topic to which the LWT message will be published */
+ const char* message; /**< The LWT payload */
+ uint8_t retained; /**< The retained flag for the LWT message */
+ uint8_t qos; /**< The quality of service setting for the LWT message */
+} MQTTPacketWillOptions;
+```
+
+- 宏注释:对于需要注释的宏,需要在之前使用以下格式才能显示在`doxygen`中
+
+```
+/**
+ * @brief header 1 byte + remaining length 1~4 byte(s).
+ *
+ */
+#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
+```
+
+---
+
+### 格式
+
+除了命名,格式请以`clang-format`格式化结果为准,可参考[SDK代码格式说明](./SDK代码格式说明.md)
+
+#### 行长度
+
+每一行代码最大字符数为 120。
+
+#### 非ASCII字符
+
+尽量不使用非 ASCII 字符,使用时必须使用 UTF-8 编码。
+非 ASCII 字符(中文等)只能用于注释当中,含有中文注释的代码文件必须使用 UTF-8 编码保存,不能有BOM头。
+
+#### 空格
+
+只使用空格,每次缩进 4 个空格。
+不要在代码中使用制表符。你应该设置编辑器将制表符转为空格。
+
+#### 函数声明与定义
+
+返回类型和函数名在同一行,参数也尽量放在同一行,如果放不下就对形参分行,分行方式与函数调用一致。
+
+- 使用好的参数名。
+- 只有在参数未被使用或者其用途非常明显时,才能省略参数名。
+- 如果返回类型和函数名在一行放不下,分行。
+- 如果返回类型与函数声明或定义分行了,不要缩进。
+- 左圆括号总是和函数名在同一行。
+- 函数名和左圆括号间永远没有空格。
+- 圆括号与参数间没有空格。
+- 左大括号总在最后一个参数同一行的末尾处,不另起新行。
+- 右大括号总是单独位于函数最后一行,或者与左大括号同一行。
+- 右圆括号和左大括号间总是有一个空格。
+- 所有形参应尽可能对齐。
+- 缺省缩进为 4 个空格。
+- 换行后的参数保持 4 个空格的缩进。
+
+#### 浮点字面量
+
+浮点字面常量都要带小数点,小数点两边都要有数字,即使它们使用指数表示法。如果所有浮点数都采用这种熟悉的形式,可以提高可读性,不会被误认为是整数,指数符号的E/e不会被误认为十六进制数字。可以使用整数字面常量初始化浮点变量(假设变量类型可以精确地表示该整数),但是请注意,指数表示法中的数字绝不能用整数。
+
+```c
+float f = 1.0f;
+float f2 = 1; // 也可以用整数字面常量初始化浮点数
+long double ld = -0.5L;
+double d = 1248.0e6;
+```
+
+#### 函数调用
+
+允许3种格式,在一行内写完函数调用,在圆括号里对参数分行,或者参数另起一行且缩进四格。
+原则上,尽可能精简行数,比如把多个参数适当地放在同一行里。
+
+函数调用遵循如下形式:
+
+```c
+bool retval = DoSomething(argument1, argument2, argument3);
+```
+
+如果同一行放不下,可断为多行,后面每一行都和第一个实参对齐,左圆括号后和右圆括号前不要留空格:
+
+```c
+bool retval = DoSomething(averyveryveryverylongargument1,
+ argument2, argument3);
+```
+
+参数也可以放在次行,缩进四格:
+
+```c
+if (...) {
+ ...
+ ...
+ if (...) {
+ DoSomething(
+ argument1, argument2, // 4 空格缩进
+ argument3, argument4);
+ }
+```
+
+除非影响到可读性,尽量把多个参数放在同一行,以减少函数调用所需的行数。有人认为把每个参数都独立成行,不仅更好读,而且方便编辑参数
+
+#### 列表初始化
+
+列表初始化格式,函数调用如何格式化,就如何格式化列表初始化。
+
+#### 条件语句
+
+建议不在圆括号内使用空格。关键字 if 和 else 另起一行。
+在所有情况下,if 和左圆括号间都要有个空格。右圆括号和左大括号之间也要有个空格
+
+```c
+if (condition) { // 好 - IF 和 { 都与空格紧邻
+```
+
+如果能增强可读性,简短的条件语句允许写在同一行。只有在语句简单并且没有使用 else 子句时允许使用:
+
+```c
+if (x == kFoo) return new Foo();
+if (x == kBar) return new Bar();
+```
+
+通常,单行语句不需要使用大括号,如果用也没问题;复杂的条件或循环语句用大括号可读性会更好。也有一些项目要求 if必须总是使用大括号
+
+```c
+if (condition) {
+ DoSomething(); // 4 空格缩进.
+}
+```
+
+#### 循环和开关选择语句
+
+switch 语句中的 case 块可以使用大括号也可以不用,取决于个人选择。如果有不满足 case 条件的枚举值,switch 应该总是包含一个 default匹配 (如果有输入值没有 case 去处理, 编译器将给出 warning)。如果default 永远执行不到,简单的使用 assert语句。
+
+```c
+switch (var) {
+ case 0: { // 2 空格缩进
+ ... // 4 空格缩进
+ break;
+ }
+ case 1: {
+ ...
+ break;
+ }
+ default: {
+ assert(false);
+ }
+}
+```
+
+空循环体应使用 {} 或 continue,而不是一个简单的分号。
+
+#### 指针和引用表达式
+
+句点或箭头前后不要有空格。 `(*, &)` 作为指针/地址操作符时之后不能有空格。`(*, &)`在用于声明指针变量或参数时,空格前置后置都可以。
+
+#### 布尔表达式
+
+如果一个布尔表达式超过标准行宽 ``,断行方式要统一。比如,逻辑操作符要么都在行尾,要么都在行首。
+
+#### 返回值
+
+不要在 return 表达式里加上非必须的圆括号。
+
+#### 变量及数组初始化
+
+变量及数组初始化用 =,() 和 {} 均可。
+
+#### 预处理指令
+
+预处理指令不要缩进,从行首开始。即使预处理指令位于缩进代码块中,指令也应从行首开始。
+
+```c
+// 好 - 指令从行首开始
+ if (lopsided_score) {
+#if DISASTER_PENDING // 正确 - 从行首开始
+ DropEverything();
+# if NOTIFY // 非必要 - # 后跟空格
+ NotifyClient();
+# endif
+#endif
+ BackToNormal();
+ }
+```
+
+#### 水平留白
+
+水平留白的使用取决于代码的位置。永远不要在行尾添加没意义的留白。
+
+#### 垂直留白
+
+这不仅仅是规则而是原则问题: 不在万不得已,不要使用空行,尤其是:两个函数定义之间的空行不要超过 2 行,函数体首尾不要留空行,函数体中也不要随意添加空行。
+基本原则是: 一屏显示的代码越多,程序的控制流越容易理解。当然,过于密集的代码块和过于疏松的代码块同样难看,这取决于你的判断,但通常是垂直留白越少越好。
+下面的规则可以让加入的空行更有效:
+
+- 函数体内开头或结尾的空行可读性微乎其微。
+- 在多重 if-else 块里加空行或许有点可读性。
diff --git a/components/connectivity/iot-hub-device-c-sdk/docs/数据模板开发/数据模板代码生成.md b/components/connectivity/iot-hub-device-c-sdk/docs/数据模板开发/数据模板代码生成.md
new file mode 100644
index 00000000..4281f115
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/docs/数据模板开发/数据模板代码生成.md
@@ -0,0 +1,34 @@
+
+
+- [简介](#%E7%AE%80%E4%BB%8B)
+- [数据模板创建](#%E6%95%B0%E6%8D%AE%E6%A8%A1%E6%9D%BF%E5%88%9B%E5%BB%BA)
+- [数据模板描述文件导出](#%E6%95%B0%E6%8D%AE%E6%A8%A1%E6%9D%BF%E6%8F%8F%E8%BF%B0%E6%96%87%E4%BB%B6%E5%AF%BC%E5%87%BA)
+- [数据模板模板代码生成](#%E6%95%B0%E6%8D%AE%E6%A8%A1%E6%9D%BF%E6%A8%A1%E6%9D%BF%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90)
+
+
+## 简介
+
+本文介绍基于物联开发平台 IoT Explorer 创建的数据模板如何生成模板代码。
+
+## 数据模板创建
+
+参阅[产品定义](https://cloud.tencent.com/document/product/1081/34739?!preview&!editLang=zh#.E6.95.B0.E6.8D.AE.E6.A8.A1.E6.9D.BF)创建数据模板
+
+## 数据模板描述文件导出
+
+数据模板描述文件是一个 JSON 格式的文件,描述了产品定义的属性、事件及其他信息,在平台导出此json文件,如下图示:
+
+
+
+## 数据模板模板代码生成
+
+将下载的json文件拷贝到tools目录,执行`./data_template_codegen.py -c xx/example_config.json -d ../targetdir/`命令,则会根据json文件在target目录生成所定义产品的数据模板及事件的配置文件,
+将这个生成的文件拷贝到`app/data_template`的目录下,
+data_template_app.c 示例了智能灯的数据模板处理框架,可以基于此框架修改业务逻辑。
+
+```bash
+./data_template_codegen.py -c example_config.json
+加载 .\example_config.json 文件成功
+文件 ./data_template_config_header.include 生成成功
+文件 ./data_template_config_src_c.include 生成成功
+```
diff --git a/components/connectivity/iot-hub-device-c-sdk/docs/数据模板开发/数据模板协议.md b/components/connectivity/iot-hub-device-c-sdk/docs/数据模板开发/数据模板协议.md
new file mode 100644
index 00000000..e7f25cb8
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/docs/数据模板开发/数据模板协议.md
@@ -0,0 +1,340 @@
+
+
+- [简介](#%E7%AE%80%E4%BB%8B)
+- [数据模板协议](#%E6%95%B0%E6%8D%AE%E6%A8%A1%E6%9D%BF%E5%8D%8F%E8%AE%AE)
+ - [概述](#%E6%A6%82%E8%BF%B0)
+ - [设备属性上报](#%E8%AE%BE%E5%A4%87%E5%B1%9E%E6%80%A7%E4%B8%8A%E6%8A%A5)
+ - [设备远程控制](#%E8%AE%BE%E5%A4%87%E8%BF%9C%E7%A8%8B%E6%8E%A7%E5%88%B6)
+ - [获取设备最新上报信息](#%E8%8E%B7%E5%8F%96%E8%AE%BE%E5%A4%87%E6%9C%80%E6%96%B0%E4%B8%8A%E6%8A%A5%E4%BF%A1%E6%81%AF)
+ - [设备事件上报](#%E8%AE%BE%E5%A4%87%E4%BA%8B%E4%BB%B6%E4%B8%8A%E6%8A%A5)
+ - [设备行为调用](#%E8%AE%BE%E5%A4%87%E8%A1%8C%E4%B8%BA%E8%B0%83%E7%94%A8)
+
+
+## 简介
+
+用户创建完产品后即可定义数据模板,基于导出数据模板json配置文件通过SDK提供的脚本工具可以自动生成数据模板配置代码,设备调试阶段的在线调试会接收设备的上报数据并可在控制台下发控制指令到设备进行调试。本文档介绍数据模板协议。
+
+## 数据模板协议
+
+### 概述
+
+物联网开发平台定义了一套通用的方法,实现设备的统一描述、统一控制,进而提供数据的流转和计算服务,实现不同设备的互联互通、数据的流转和融合,助力应用落地。
+
+产品定义了数据模板以后,设备可以按照数据模板中的定义上报属性、事件,并可对设备下发远程控制指令,即对可写的设备属性进行修改。数据模板的管理详见 [产品定义](https://cloud.tencent.com/document/product/1081/34739)。数据模板协议包括了以下几部分。
+
+- 设备属性上报:设备端将定义的属性根据设备端的业务逻辑向云端上报。
+- 设备远程控制:从云端向设备端下发控制指令,即设置设备可写属性。
+- 获取设备最新上报信息:获取设备最新的上报数据。
+- 设备事件上报:设备可根据定义的数据模板中的事件,当事件被触发,则根据设备事件上报的协议上报告警、故障等事件信息。
+- 设备行为:云端可以通过 RPC 的方式来要求设备执行某个动作行为,适用于应用需要实时获取设备的执行结果的场景。
+
+### 设备属性上报
+
+1.当设备需要向云端上报数据时,开发平台为设备设定了默认的 Topic
+
+- 上行请求 Topic:`$thing/up/property/{ProductID}/{DeviceName}`
+- 上行响应 Topic:`$thing/down/property/{ProductID}/{DeviceName}`
+
+2.请求
+
+- 设备端请求报文示例:
+
+ ```json
+ {
+ "method":"report",
+ "clientToken":"123",
+ "timestamp":1212121221,
+ "params":{
+ "power_switch":1,
+ "color":1,
+ "brightness":32
+ }
+ }
+ ```
+
+- 参数说明:
+
+ |参数|类型|说明|
+ |---|---|---|
+ |method|String|`report`表示设备属性上报|
+ |clientToken|String|用于上下行消息配对标识|
+ |timestamp|Integer|属性上报的时间,SDK默认省略|
+ |params|JSON|JSON 结构内为设备上报的属性值|
+
+- sdk接口:`IOT_DataTemplate_PropertyReport`
+
+3.响应
+
+- 云端返回设备端报文示例:
+
+ ```json
+ {
+ "method":"report_reply",
+ "clientToken":"123",
+ "code":0,
+ "status":"some message where error"
+ }
+ ```
+
+- 响应参数说明:
+
+ |参数|类型|说明|
+ |---|---|---|
+ |method|String|`report_reply`表示云端接收设备上报后的响应报文|
+ |clientToken|String|用于上下行消息配对标识|
+ |code|Integer|`0`表示云端成功收到设备上报的属性|
+ |status|String|当code非0的时候, 提示错误信息|
+
+- sdk回调:`PropertyMessageCallback.method_control_callback`
+
+### 设备远程控制
+
+1.云端下发控制指令使用的 Topic:
+
+- 下发 Topic:`$thing/down/property/{ProductID}/{DeviceName}`
+- 响应 Topic:`$thing/up/property/{ProductID}/{DeviceName}`
+
+2.请求
+
+- 远程控制请求消息格式:
+
+ ```json
+ {
+ "method": "control",
+ "clientToken": "123",
+ "params": {
+ "power_switch": 1,
+ "color": 1,
+ "brightness": 66
+ }
+ }
+ ```
+
+- 请求参数说明:
+
+ |参数|类型|说明|
+ |---|---|---|
+ |method|String|control 表示云端向设备发起控制请求|
+ |clientToken|String|用于上下行消息配对标识|
+ |timestamp|Integer|属性上报的时间,SDK默认省略|
+ |params|JSON|JSON 结构内为设备上报的属性值|
+
+- sdk回调:`PropertyMessageCallback.method_control_callback`
+
+3.响应
+
+- 设备响应远程控制请求消息格式:
+
+ ```json
+ {
+ "method":"control_reply",
+ "clientToken":"123",
+ "code":0,
+ "status":"some message when error"
+ }
+ ```
+
+- 请求参数说明:
+
+ |参数|类型|说明|
+ |---|---|---|
+ |method|String|`control_reply`表示设备向云端下发的控制指令的请求响应|
+ |clientToken|String|用于上下行消息配对标识|
+ |code|Integer|0 表示设备成功接收到云端下发的控制指令|
+ |status|String|当code非0的时候, 提示错误信息|
+
+- sdk接口:`IOT_DataTemplate_PropertyControlReply`
+
+### 获取设备最新上报信息
+
+1.设备从云端接收最新消息使用的 Topic:
+
+- 请求 Topic:`$thing/up/property/{ProductID}/{DeviceName}`
+- 响应 Topic:`$thing/down/property/{ProductID}/{DeviceName}`
+
+2.请求
+
+- 请求消息格式:
+
+ ```json
+ {
+ "method": "get_status",
+ "clientToken": "123",
+ "type" : "control",
+ "showmeta": 0
+ }
+ ```
+
+- 请求参数说明:
+
+ |参数|类型|说明|
+ |---|---|---|
+ |method|String|`get_status`表示获取设备最新上报的信息|
+ |clientToken|String|消息ID,回复的消息将会返回该数据,用于请求响应消息的对比|
+ |type|String|表示获取什么类型的信息。`control`表示离线下发的消息;`report`表示设备上报的信息。SDK默认省略,获取所有消息|
+ |showmeta|Integer|标识回复消息是否带`metadata`,缺省为0表示不返回`metadata`,主要是时间戳信息。SDK默认省略|
+
+- sdk接口:`IOT_DataTemplate_PropertyGetStatus`
+
+3.响应
+
+- 响应消息格式:
+
+ ```json
+ {
+ "method": "get_status_reply",
+ "clientToken": "123",
+ "code": 0,
+ "type": "report",
+ "data": {
+ "reported": {
+ "power_switch": 1,
+ "color": 1,
+ "brightness": 66
+ }
+ }
+ }
+ ```
+
+- 响应参数
+
+ |参数|类型|说明|
+ |---|---|---|
+ |method|String|get_status_reply 表示获取设备最新上报信息的 reply 消息|
+ |clientToken|String|消息 ID,回复的消息将会返回该数据,用于请求响应消息的对比|
+ |code|Integer|0标识云端成功收到设备上报的属性|
+ |type|String|表示获取什么类型的信息。control 表示离线下发的消息;report 表示设备上报的信息|
+ |data|JSON|返回具体设备上报的最新数据内容|
+
+- sdk回调:`PropertyMessageCallback.method_get_status_reply_callback`
+
+### 设备事件上报
+
+1.当设备需要向云端上报事件时,开发平台为设备设定了默认的 Topic:
+
+- 上行请求 Topic:`$thing/up/event/<产品>/<设备>`
+- 上行响应 Topic:`$thing/down/event/<产品>/<设备>`
+
+2.请求
+
+- 设备端请求报文示例:
+
+ ```json
+ {
+ "method":"event_post",
+ "clientToken":"123",
+ "version":"1.0",
+ "eventId":"PowerAlarm",
+ "type":"fatal",
+ "timestamp":1212121221,
+ "params":{
+ "Voltage":2.8,
+ "Percent":20
+ }
+ }
+ ```
+
+- 请求参数说明:
+
+ |参数|类型|说明|
+ |---|---|---|
+ |method|String|`event_post`表示事件上报|
+ |clientToken|String|消息 ID,回复的消息将会返回该数据,用于请求响应消息的对比|
+ |version|String|协议版本,默认为1.0|
+ |eventId|String|事件的 Id,在物模型事件中定义|
+ |type|String|事件类型,`info`:信息;`alert`:告警;`fault`:故障|
+ |timestamp|String|事件的参数,在物模型事件中定义|
+ |params|String|事件的参数,在物模型事件中定义|
+
+- sdk接口:`IOT_DataTemplate_EventPost`
+
+3.响应
+
+- 响应消息格式:
+
+ ```json
+ {
+ "method": "event_reply",
+ "clientToken": "123",
+ "version": "1.0",
+ "code": 0,
+ "status": "some message where error",
+ "data": {}
+ }
+ ```
+
+- 响应参数:
+
+ |参数|类型|说明|
+ |---|---|---|
+ |method|String|`event_reply`表示是云端返回设备端的响应|
+ |clientToken|String|消息 ID,回复的消息将会返回该数据,用于请求响应消息的对比|
+ |version|String|协议版本,默认为1.0|
+ |code|Integer|事件上报结果,0表示成功|
+ |status|String|事件上报结果描述|
+ |data|JSON|事件上报返回的内容|
+
+- sdk回调:`EventMessageCallback.method_event_reply_callback`
+
+### 设备行为调用
+
+1.当应用通过云端向设备发起某个行为调用时,开发平台为设备行为的处理设定了默认的 Topic:
+
+- 应用调用设备行为 Topic:`$thing/down/action/<产品>/<设备>`
+- 设备响应行为执行结果 Topic:`$thing/up/action/<产品>/<设备>`
+
+2.请求
+
+- 应用端发起设备行为调用报文示例:
+
+ ```json
+ {
+ "method": "action",
+ "clientToken": "20a4ccfd-d308-11e9-86c6-5254008a4f10",
+ "actionId": "openDoor",
+ "timestamp": 1212121221,
+ "params": {
+ "userid": "323343"
+ }
+ }
+ ```
+
+- 请求参数说明:
+
+ |参数|类型|说明|
+ |---|---|---|
+ |method|String|`action`表示是调用设备的某个行为|
+ |clientToken|String|消息 ID,回复的消息将会返回该数据,用于请求响应消息的对比|
+ |actionId|String|`actionId`是数据模板中的行为标识符,由开发者自行根据设备的应用场景定义|
+ |timestamp|Integer|行为调用的当前时间,SDK默认省略|
+ |params|String|行为的调用参数,在数据模板的行为中定义|
+
+- sdk回调:`ActionMessageCallback.method_action_callback`
+
+3.响应
+
+- 响应消息格式:
+
+ ```json
+ {
+ "method": "action_reply",
+ "clientToken": "20a4ccfd-d308-11e9-86c6-5254008a4f10",
+ "code": 0,
+ "status": "some message where error",
+ "response": {
+ "Code": 0
+ }
+ }
+ ```
+
+- 响应参数:
+
+ |参数|类型|说明|
+ |---|---|---|
+ |method|String|`action_reply`表示是设备端执行完指定的行为向云端回复的响应|
+ |clientToken|String|消息 ID,回复的消息将会返回该数据,用于请求响应消息的对比|
+ |code|Integer|行为执行结果,0表示成功|
+ |status|String|行为执行失败后的错误信息描述|
+ |response|String|设备行为中定义的返回参数,设备行为执行成功后,向云端返回执行结果|
+
+- sdk接口:`IOT_DataTemplate_ActionReply`
diff --git a/components/connectivity/iot-hub-device-c-sdk/docs/数据模板开发/数据模板应用开发.md b/components/connectivity/iot-hub-device-c-sdk/docs/数据模板开发/数据模板应用开发.md
new file mode 100644
index 00000000..0d4c7488
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/docs/数据模板开发/数据模板应用开发.md
@@ -0,0 +1,162 @@
+
+
+- [数据模板业务逻辑开发发](#%E6%95%B0%E6%8D%AE%E6%A8%A1%E6%9D%BF%E4%B8%9A%E5%8A%A1%E9%80%BB%E8%BE%91%E5%BC%80%E5%8F%91%E5%8F%91)
+ - [下行业务逻辑实现](#%E4%B8%8B%E8%A1%8C%E4%B8%9A%E5%8A%A1%E9%80%BB%E8%BE%91%E5%AE%9E%E7%8E%B0)
+ - [上行业务逻辑实现现](#%E4%B8%8A%E8%A1%8C%E4%B8%9A%E5%8A%A1%E9%80%BB%E8%BE%91%E5%AE%9E%E7%8E%B0%E7%8E%B0)
+
+
+## 数据模板业务逻辑开发发
+
+数据模板示例`data_template_app.c`已实现数据、事件收发及响应的通用处理框架。可以基于此示例开发业务逻辑,
+上下行业务逻辑处理的入口函数分别为 `_usr_init`、`_method_control_callback` 、`_cycle_report`、`_method_action_callback`。
+
+- 属性
+ 属性即对设备能力的描述,譬如智能灯,通过 `开关`、`颜色`、`亮度`三个属性实现对智能灯的能力描述,通过对属性的修改即可实现对设备的控制。
+
+- 事件
+ 设备发生特定情况,譬如灯的开关状态发生了变化,上报事件。应用侧收到事件后按预设逻辑推送事件。
+
+- 行为
+ 控制设备执行特定的行为,并将执行的结果返回。行为与属性的区别,概念上行为是数据和方法的组合,行为有执行结果的返回。属性只有数据,修改属性后设备侧是否执行成功很难在属性本身体现。
+
+### 下行业务逻辑实现
+
+- 服务端下行的数据,SDK已按数据模板协议完成了 json 数据的解析。
+- 用户根据数据模板数据进行相应的业务逻辑处理,以下提供基于智能灯的示例,用户可以根据自己的物模型进行修改
+
+```c
+static void _handle_property_callback(void *client, int is_get_status)
+{
+ for (UsrPropertyIndex i = USR_PROPERTY_INDEX_POWER_SWITCH; i <= USR_PROPERTY_INDEX_POWER; i++) {
+ if (usr_data_template_property_status_get(i)) { // 如果状态改变,说明收到了相应的属性
+ DataTemplatePropertyValue value;
+ switch (i) {
+ case USR_PROPERTY_INDEX_POWER_SWITCH:
+ case USR_PROPERTY_INDEX_COLOR:
+ case USR_PROPERTY_INDEX_BRIGHTNESS: // read only, just for example
+ case USR_PROPERTY_INDEX_POWER: // read only, just for example
+ value = usr_data_template_property_value_get(i); // 获取相应的属性值
+ Log_d("recv %s:%d", usr_data_template_property_key_get(i), value.value_int);
+ break;
+ case USR_PROPERTY_INDEX_NAME: // read only, just for example
+ value = usr_data_template_property_value_get(i);
+ Log_d("recv %s:%s", usr_data_template_property_key_get(i), value.value_string);
+ break;
+ case USR_PROPERTY_INDEX_POSITION: // read only, just for example
+ for (UsrPropertyPositionIndex j = USR_PROPERTY_POSITION_INDEX_LONGITUDE;
+ j <= USR_PROPERTY_POSITION_INDEX_LATITUDE; j++) {
+ value = usr_data_template_property_struct_value_get(i, j);
+ Log_d("recv %s:%d", usr_data_template_property_struct_key_get(i, j), value.value_int);
+ }
+ break;
+ default:
+ break;
+ }
+ usr_data_template_property_status_reset(i); //处理完需要重置属性状态
+ }
+ }
+}
+```
+
+```c
+static void _method_action_callback(UtilsJsonValue client_token, UtilsJsonValue action_id, UtilsJsonValue params,
+ void *usr_data)
+{
+ UsrActionIndex index;
+ int rc;
+ DataTemplatePropertyValue value_time, value_color, value_total_time;
+
+ char buf[256];
+
+ Log_i("recv msg[%.*s]: action_id=%.*s|params=%.*s", client_token.value_len, client_token.value, action_id.value_len,
+ action_id.value, params.value_len, params.value);
+
+ // 数据模板行为 json 格式解析
+ rc = usr_data_template_action_parse(action_id, params, &index);
+ if (rc) {
+ return;
+ }
+
+ // 修改和添加下行行为数据的具体业务逻辑
+ switch (index) {
+ case USR_ACTION_INDEX_LIGHT_BLINK:
+ value_time = usr_data_template_action_input_value_get(USR_ACTION_INDEX_LIGHT_BLINK,
+ USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME);
+ value_color = usr_data_template_action_input_value_get(USR_ACTION_INDEX_LIGHT_BLINK,
+ USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR);
+ value_total_time = usr_data_template_action_input_value_get(USR_ACTION_INDEX_LIGHT_BLINK,
+ USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME);
+ Log_i("light[%d] blink %d every %d s ", value_color.value_enum, value_time.value_int,
+ value_total_time.value_int);
+
+ // 下行行为数据处理结果的回复
+ usr_data_template_action_reply(usr_data, buf, sizeof(buf), index, client_token, 0, "{\"err_code\":0}");
+ break;
+ default:
+ break;
+ }
+}
+```
+
+### 上行业务逻辑实现现
+
+- SDK提供了属性、事件、行为的上行接口,用户可以按数据模板协议自行构造数据,然后调用接口上报相应数据。
+
+ 1. 属性上报接口`usr_data_template_property_report`
+ 2. 事件上报接口`usr_data_template_event_post`
+ 3. 行为回复接口`usr_data_template_action_reply`
+
+- 数据模板示例 data_template_app.c 已提供了属性、事件、行为数据构造和上报的框架。
+
+ 1.对于属性上报,调用修改对应属性参数的接口 `usr_data_template_property_value_set`,然后调用属性上报接口`usr_data_template_property_report`会将需要上报的属性信息上报。
+
+```c
+// 参考 _usr_init 函数,设备在业务运行过程中修改属性后需要调用以下对应函数修改对应的属性
+// usr_data_template_property_report(client, buf, sizeof(buf)); 函数进行属性数据的上报
+
+static void _usr_init(void)
+{
+ usr_data_template_init();
+
+ DataTemplatePropertyValue value;
+ value.value_int = 0;
+ usr_data_template_property_value_set(USR_PROPERTY_INDEX_POWER_SWITCH, value);
+ value.value_enum = 0;
+ usr_data_template_property_value_set(USR_PROPERTY_INDEX_COLOR, value);
+ value.value_int = 10;
+ usr_data_template_property_value_set(USR_PROPERTY_INDEX_BRIGHTNESS, value);
+ value.value_string = "light";
+ usr_data_template_property_value_set(USR_PROPERTY_INDEX_NAME, value);
+ value.value_int = 30;
+ usr_data_template_property_struct_value_set(USR_PROPERTY_INDEX_POSITION, USR_PROPERTY_POSITION_INDEX_LONGITUDE,
+ value);
+ value.value_int = 30;
+ usr_data_template_property_struct_value_set(USR_PROPERTY_INDEX_POSITION, USR_PROPERTY_POSITION_INDEX_LATITUDE,
+ value);
+ value.value_string = "high";
+ usr_data_template_property_value_set(USR_PROPERTY_INDEX_POWER, value);
+}
+```
+
+ 2.对于事件,在事件产生的地方,构造事件的 json 串并调用事件上报接口`usr_data_template_event_post`上报。
+
+```c
+static void _cycle_report(void *client)
+{
+ char buf[256];
+ static QcloudIotTimer sg_cycle_report_timer;
+ if (IOT_Timer_Expired(&sg_cycle_report_timer)) {
+
+ // 添加和修改业务需要上报的事件数据
+ usr_data_template_event_post(client, buf, sizeof(buf), USR_EVENT_INDEX_STATUS_REPORT,
+ "{\"status\":0,\"message\":\"ok\"}");
+ IOT_Timer_Countdown(&sg_cycle_report_timer, 60);
+ }
+}
+```
+
+ 3.对于行为,在行为的回调 `_method_action_callback` 中,添加业务逻辑执行对应的行为,根据行为执行结果,构造行为的输出参数,调用行为回复接口`usr_data_template_action_reply`上报行为执行结果。
+
+ ```c
+ usr_data_template_action_reply(usr_data, buf, sizeof(buf), index, client_token, 0, "{\"err_code\":0}");
+ ```
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/common/qcloud_iot_debug.h b/components/connectivity/iot-hub-device-c-sdk/include/common/qcloud_iot_debug.h
new file mode 100644
index 00000000..dfffc5bb
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/common/qcloud_iot_debug.h
@@ -0,0 +1,72 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_debug.h
+ * @brief iot debug & upload debug
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-28
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-28 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_DEBUG_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_DEBUG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "utils_log.h"
+#include "qcloud_iot_config.h"
+#include "qcloud_iot_platform.h"
+
+#ifdef IOT_DEBUG
+#define IOT_FUNC_ENTRY \
+ { \
+ Log_d("FUNC_ENTRY: %s L#%d \n", __FUNCTION__, __LINE__); \
+ }
+#define IOT_FUNC_EXIT \
+ { \
+ Log_d("FUNC_EXIT: %s L#%d \n", __FUNCTION__, __LINE__); \
+ return; \
+ }
+#define IOT_FUNC_EXIT_RC(x) \
+ { \
+ Log_d("FUNC_EXIT: %s L#%d Return Code : %d \n", __FUNCTION__, __LINE__, x; \
+ return x; \
+ }
+#else
+#define IOT_FUNC_ENTRY
+#define IOT_FUNC_EXIT \
+ { \
+ return; \
+ }
+#define IOT_FUNC_EXIT_RC(x) \
+ { \
+ return x; \
+ }
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_DEBUG_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/common/qcloud_iot_device.h b/components/connectivity/iot-hub-device-c-sdk/include/common/qcloud_iot_device.h
new file mode 100644
index 00000000..8c90347a
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/common/qcloud_iot_device.h
@@ -0,0 +1,114 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_device.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-28
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-28 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_DEVICE_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_DEVICE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include "qcloud_iot_config.h"
+
+/**
+ * @brief MAX size of client ID.
+ *
+ */
+#define MAX_SIZE_OF_CLIENT_ID (80)
+
+/**
+ * @brief MAX size of product ID.
+ *
+ */
+#define MAX_SIZE_OF_PRODUCT_ID (10)
+
+/**
+ * @brief MAX size of product secret.
+ *
+ */
+#define MAX_SIZE_OF_PRODUCT_SECRET (32)
+
+/**
+ * @brief MAX size of device name.
+ *
+ */
+#define MAX_SIZE_OF_DEVICE_NAME (48)
+
+/**
+ * @brief MAX size of device secret.
+ *
+ */
+#define MAX_SIZE_OF_DEVICE_SECRET (64)
+
+/**
+ * @brief MAX size of device cert file name.
+ *
+ */
+#define MAX_SIZE_OF_DEVICE_CERT_FILE_NAME (128)
+
+/**
+ * @brief MAX size of device key file name.
+ *
+ */
+#define MAX_SIZE_OF_DEVICE_SECRET_FILE_NAME (128)
+
+/**
+ * @brief Max size of base64 encoded PSK = 64, after decode: 64/4*3 = 48.
+ *
+ */
+#define MAX_SIZE_OF_DECODE_PSK_LENGTH 48
+
+/**
+ * @brief Device info needed to connect mqtt server.
+ *
+ */
+typedef struct {
+ char product_id[MAX_SIZE_OF_PRODUCT_ID + 1];
+ char device_name[MAX_SIZE_OF_DEVICE_NAME + 1];
+ char client_id[MAX_SIZE_OF_CLIENT_ID + 1];
+
+#ifdef AUTH_MODE_CERT
+ char dev_cert_file_name[MAX_SIZE_OF_DEVICE_CERT_FILE_NAME + 1];
+ char dev_key_file_name[MAX_SIZE_OF_DEVICE_SECRET_FILE_NAME + 1];
+#else
+ char device_secret[MAX_SIZE_OF_DEVICE_SECRET + 1];
+ uint8_t device_secret_decode[MAX_SIZE_OF_DECODE_PSK_LENGTH];
+ size_t device_secret_decode_len;
+#endif
+
+#ifdef DEV_DYN_REG_ENABLED
+ char product_secret[MAX_SIZE_OF_PRODUCT_SECRET + 1];
+#endif
+} DeviceInfo;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_DEVICE_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/common/qcloud_iot_error.h b/components/connectivity/iot-hub-device-c-sdk/include/common/qcloud_iot_error.h
new file mode 100644
index 00000000..586dfde4
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/common/qcloud_iot_error.h
@@ -0,0 +1,97 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_error.h
+ * @brief error code of sdk
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-28
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-28 1.0 fancyxu first commit
+ * 2021-07-08 1.1 fancyxu fix code standard of IotReturnCode and QcloudIotClient
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_ERROR_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_ERROR_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief IOT SDK return/error code.
+ * Enumeration of return code in QCloud IoT C-SDK.
+ * Values less than 0 are specific error codes
+ * Value of 0 is successful return
+ * Values greater than 0 are specific non-error return codes
+ *
+ */
+typedef enum {
+ QCLOUD_RET_SUCCESS = 0, /**< Successful return */
+ QCLOUD_ERR_FAILURE = -1, /**< Generic failure return */
+ QCLOUD_ERR_INVAL = -2, /**< Invalid parameter */
+ QCLOUD_ERR_DEV_INFO = -3, /**< Fail to get device info */
+ QCLOUD_ERR_MALLOC = -4, /**< Fail to malloc memory */
+ QCLOUD_RET_MQTT_ALREADY_CONNECTED = 4, /**< Already connected with MQTT server */
+ QCLOUD_RET_MQTT_CONNACK_CONNECTION_ACCEPTED = 3, /**< MQTT connection accepted by server */
+ QCLOUD_RET_MQTT_MANUALLY_DISCONNECTED = 2, /**< Manually disconnected with MQTT server */
+ QCLOUD_RET_MQTT_RECONNECTED = 1, /**< Reconnected with MQTT server successfully */
+ QCLOUD_ERR_MQTT_NO_CONN = -101, /**< Not connected with MQTT server */
+ QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT = -102, /**< Reconnecting with MQTT server */
+ QCLOUD_ERR_MQTT_RECONNECT_TIMEOUT = -103, /**< MQTT reconnect timeout */
+ QCLOUD_ERR_MQTT_SUB = -104, /**< MQTT topic subscription fail */
+ QCLOUD_ERR_MQTT_NOTHING_TO_READ = -105, /**< MQTT nothing to read */
+ QCLOUD_ERR_MQTT_PACKET_READ = -106, /**< Something wrong when reading MQTT packet */
+ QCLOUD_ERR_MQTT_REQUEST_TIMEOUT = -107, /**< MQTT request timeout */
+ QCLOUD_ERR_RX_MESSAGE_INVAL = -108, /**< MQTT received invalid msg */
+ QCLOUD_ERR_BUF_TOO_SHORT = -109, /**< MQTT recv buffer not enough */
+ QCLOUD_ERR_MQTT_QOS_NOT_SUPPORT = -110, /**< MQTT QoS level not supported */
+ QCLOUD_ERR_MQTT_UNSUB_FAIL = -111, /**< MQTT unsubscribe failed */
+ QCLOUD_ERR_MAX_TOPIC_LENGTH = -112, /**< Topic length oversize */
+ QCLOUD_ERR_HTTP = -201, /**< HTTP unknown error */
+ QCLOUD_ERR_HTTP_AUTH = -202, /**< HTTP auth failed */
+ QCLOUD_ERR_HTTP_NOT_FOUND = -203, /**< HTTP 404 */
+ QCLOUD_ERR_HTTP_TIMEOUT = -204, /**< HTTP timeout */
+ QCLOUD_ERR_HTTP_PARSE = -205, /**< HTTP URL parse failed */
+ QCLOUD_ERR_JSON_PARSE = -300, /**< JSON parsing error */
+ QCLOUD_ERR_TCP_SOCKET_FAILED = -401, /**< TLS TCP socket connect fail */
+ QCLOUD_ERR_TCP_UNKNOWN_HOST = -402, /**< TCP unknown host (DNS fail) */
+ QCLOUD_ERR_TCP_CONNECT = -403, /**< TCP/UDP socket connect fail */
+ QCLOUD_ERR_TCP_READ_TIMEOUT = -404, /**< TCP read timeout */
+ QCLOUD_ERR_TCP_WRITE_TIMEOUT = -405, /**< TCP write timeout */
+ QCLOUD_ERR_TCP_READ_FAIL = -406, /**< TCP read error */
+ QCLOUD_ERR_TCP_WRITE_FAIL = -407, /**< TCP write error */
+ QCLOUD_ERR_TCP_PEER_SHUTDOWN = -408, /**< TCP server close connection */
+ QCLOUD_ERR_TCP_NOTHING_TO_READ = -409, /**< TCP socket nothing to read */
+ QCLOUD_ERR_SSL_INIT = -501, /**< TLS/SSL init fail */
+ QCLOUD_ERR_SSL_CERT = -502, /**< TLS/SSL certificate issue */
+ QCLOUD_ERR_SSL_CONNECT = -503, /**< TLS/SSL connect fail */
+ QCLOUD_ERR_SSL_CONNECT_TIMEOUT = -504, /**< TLS/SSL connect timeout */
+ QCLOUD_ERR_SSL_WRITE_TIMEOUT = -505, /**< TLS/SSL write timeout */
+ QCLOUD_ERR_SSL_WRITE = -506, /**< TLS/SSL write error */
+ QCLOUD_ERR_SSL_READ_TIMEOUT = -507, /**< TLS/SSL read timeout */
+ QCLOUD_ERR_SSL_READ = -508, /**< TLS/SSL read error */
+ QCLOUD_ERR_SSL_NOTHING_TO_READ = -509, /**< TLS/SSL nothing to read */
+} IotReturnCode;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_ERROR_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/common/qcloud_iot_params_check.h b/components/connectivity/iot-hub-device-c-sdk/include/common/qcloud_iot_params_check.h
new file mode 100644
index 00000000..3789d42b
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/common/qcloud_iot_params_check.h
@@ -0,0 +1,100 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_params_check.h
+ * @brief check params
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-28
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-28 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_PARAMS_CHECK_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_PARAMS_CHECK_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include "utils_log.h"
+
+#define NUMBERIC_SANITY_CHECK(num, err) \
+ do { \
+ if (0 > (num)) { \
+ Log_e("Invalid argument, numeric 0"); \
+ return (err); \
+ } \
+ } while (0)
+
+#define NUMBERIC_SANITY_CHECK_RTN(num) \
+ do { \
+ if (0 > (num)) { \
+ Log_e("Invalid argument, numeric 0"); \
+ return; \
+ } \
+ } while (0)
+
+#define POINTER_SANITY_CHECK(ptr, err) \
+ do { \
+ if (NULL == (ptr)) { \
+ Log_e("Invalid argument, %s = %p", #ptr, ptr); \
+ return (err); \
+ } \
+ } while (0)
+
+#define POINTER_SANITY_CHECK_RTN(ptr) \
+ do { \
+ if (NULL == (ptr)) { \
+ Log_e("Invalid argument, %s = %p", #ptr, ptr); \
+ return; \
+ } \
+ } while (0)
+
+#define STRING_PTR_SANITY_CHECK(ptr, err) \
+ do { \
+ if (NULL == (ptr)) { \
+ Log_e("Invalid argument, %s = %p", #ptr, (ptr)); \
+ return (err); \
+ } \
+ if (0 == strlen((ptr))) { \
+ Log_e("Invalid argument, %s = '%s'", #ptr, (ptr)); \
+ return (err); \
+ } \
+ } while (0)
+
+#define STRING_PTR_SANITY_CHECK_RTN(ptr) \
+ do { \
+ if (NULL == (ptr)) { \
+ Log_e("Invalid argument, %s = %p", #ptr, (ptr)); \
+ return; \
+ } \
+ if (0 == strlen((ptr))) { \
+ Log_e("Invalid argument, %s = '%s'", #ptr, (ptr)); \
+ return; \
+ } \
+ } while (0)
+
+#define STRING_PTR_PRINT_SANITY_CHECK(s) ((s) ? (s) : "null")
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_PARAMS_CHECK_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/common/qcloud_iot_platform.h b/components/connectivity/iot-hub-device-c-sdk/include/common/qcloud_iot_platform.h
new file mode 100644
index 00000000..9026106f
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/common/qcloud_iot_platform.h
@@ -0,0 +1,451 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_platform.h
+ * @brief hal interface
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-28
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-28 1.0 fancyxu first commit
+ * 2021-07-09 1.1 fancyxu support tls and change port to str format
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_PLATFORM_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_PLATFORM_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "qcloud_iot_device.h"
+#include "qcloud_iot_common.h"
+
+/**********************************************************************
+ * QCloud IoT C-SDK Hardware Abstraction Layer
+ * Platform/OS/IP stack/SSL dependant functions
+ * Check platform folder for reference implementation
+ * Require porting when adapt SDK to new platform/OS
+ *********************************************************************/
+
+/**************************************************************************************
+ * os
+ **************************************************************************************/
+
+/**
+ * @brief Mutex create.
+ *
+ * @return pointer to mutex
+ */
+void *HAL_MutexCreate(void);
+
+/**
+ * @brief Mutex destroy.
+ *
+ * @param[in,out] mutex pointer to mutex
+ */
+void HAL_MutexDestroy(void *mutex);
+
+/**
+ * @brief Mutex lock.
+ *
+ * @param[in,out] mutex pointer to mutex
+ */
+void HAL_MutexLock(void *mutex);
+
+/**
+ * @brief Mutex try lock.
+ *
+ * @param[in,out] mutex pointer to mutex
+ * @return 0 for success
+ */
+int HAL_MutexTryLock(void *mutex);
+
+/**
+ * @brief Mutex unlock.
+ *
+ * @param[in,out] mutex pointer to mutex
+ */
+void HAL_MutexUnlock(void *mutex);
+
+/**
+ * @brief Malloc from heap.
+ *
+ * @param[in] size size to malloc
+ * @return pointer to buffer, NULL for failed.
+ */
+void *HAL_Malloc(size_t size);
+
+/**
+ * @brief Free buffer malloced by HAL_Malloc.
+ *
+ * @param[in] ptr
+ */
+void HAL_Free(void *ptr);
+
+/**
+ * @brief Printf with format.
+ *
+ * @param[in] fmt format
+ */
+void HAL_Printf(const char *fmt, ...);
+
+/**
+ * @brief Snprintf with format.
+ *
+ * @param[out] str buffer to save
+ * @param[in] len buffer len
+ * @param[in] fmt format
+ * @return length of formatted string, >0 for success.
+ */
+int HAL_Snprintf(char *str, const int len, const char *fmt, ...);
+
+/**
+ * @brief Sleep for ms.
+ *
+ * @param[in] ms ms to sleep
+ */
+void HAL_SleepMs(uint32_t ms);
+
+#ifdef MULTITHREAD_ENABLED
+
+/**
+ * @brief Theard entry function.
+ *
+ */
+typedef void (*ThreadRunFunc)(void *arg);
+
+/**
+ * @brief Thread priority.
+ *
+ */
+typedef enum {
+ THREAD_PRIORITY_HIGH,
+ THREAD_PRIORITY_MIDDLE,
+ THREAD_PRIORITY_LOW,
+} ThreadPriority;
+
+/**
+ * @brief Thread params to create.
+ *
+ */
+typedef struct {
+ char *thread_name; /**< thread name */
+ void* thread_id; /**< thread handle */
+ ThreadRunFunc thread_func; /**< thread entry function */
+ void *user_arg; /**< thread entry arg */
+ ThreadPriority priority; /**< thread priority */
+ void *stack_base; /**< thread stack base */
+ uint32_t stack_size; /**< thread stack size */
+} ThreadParams;
+
+/**
+ * @brief platform-dependant thread create function
+ *
+ * @param[in,out] params params to create thread @see ThreadParams
+ * @return @see IotReturnCode
+ */
+int HAL_ThreadCreate(ThreadParams *params);
+
+/**
+ * @brief platform-dependent thread destroy function.
+ *
+ */
+void HAL_ThreadDestroy(void *thread_id);
+
+/**
+ * @brief platform-dependent semaphore create function.
+ *
+ * @return pointer to semaphore
+ */
+void *HAL_SemaphoreCreate(void);
+
+/**
+ * @brief platform-dependent semaphore destory function.
+ *
+ * @param[in] sem pointer to semaphore
+ */
+void HAL_SemaphoreDestroy(void *sem);
+
+/**
+ * @brief platform-dependent semaphore post function.
+ *
+ * @param[in] sem pointer to semaphore
+ */
+void HAL_SemaphorePost(void *sem);
+
+/**
+ * @brief platform-dependent semaphore wait function.
+ *
+ * @param[in] sem pointer to semaphore
+ * @param[in] timeout_ms wait timeout
+ * @return @see IotReturnCode
+ */
+int HAL_SemaphoreWait(void *sem, uint32_t timeout_ms);
+
+/**
+ * @brief platform-dependent mail queue init function.
+ *
+ * @param[in] pool pool using in mail queue
+ * @param[in] mail_size mail size
+ * @param[in] mail_count mail count
+ * @return pointer to mail queue
+ */
+void *HAL_MailQueueInit(void *pool, size_t mail_size, int mail_count);
+
+/**
+ * @brief platform-dependent mail queue deinit function.
+ *
+ * @param[in] mail_q pointer to mail queue
+ */
+void HAL_MailQueueDeinit(void *mail_q);
+
+/**
+ * @brief platform-dependent mail queue send function.
+ *
+ * @param[in] mail_q pointer to mail queue
+ * @param[in] buf data buf
+ * @param[in] size data size
+ * @return 0 for success
+ */
+int HAL_MailQueueSend(void *mail_q, const void *buf, size_t size);
+
+/**
+ * @brief platform-dependent mail queue send function.
+ *
+ * @param[in] mail_q pointer to mail queue
+ * @param[out] buf data buf
+ * @param[in] size data size
+ * @param[in] timeout_ms
+ * @return 0 for success
+ */
+int HAL_MailQueueRecv(void *mail_q, void *buf, size_t *size, uint32_t timeout_ms);
+
+#endif
+
+/**
+ * @brief Functions for saving file into NVS(files/FLASH)
+ * @param[in] filename file path name
+ * @param[in] buf source need write buffer
+ * @param[in] write_len length of file to write
+ * @return length of data save when success, or 0 for failure
+ */
+size_t HAL_File_Write(const char *filename, const void *buf, size_t write_len, size_t offset);
+
+/**
+ * @brief Functions for reading file from NVS(files/FLASH)
+ * @param[in] filename file path name
+ * @param[in] buf destination log buffer
+ * @param[in] read_len length to read
+ * @return length of data read when success, or 0 for failure
+ */
+size_t HAL_File_Read(const char *filename, void *buf, size_t read_len, size_t offset);
+
+/**
+ * @brief Functions for deleting file in NVS(files/FLASH).
+ * @param[in] filename file path name
+ * @return 0 when success
+ */
+int HAL_File_Del(const char *filename);
+
+/**
+ * @brief Functions for reading the size of file in NVS(files/FLASH).
+ * @param[in] filename file path name
+ * @return 0 when nothing exist
+ */
+size_t HAL_File_GetSize(const char *filename);
+
+/**************************************************************************************
+ * device info
+ **************************************************************************************/
+
+/**
+ * @brief Save device info
+ *
+ * @param[in] device_info @see DeviceInfo
+ * @return @see IotReturnCode
+ */
+int HAL_SetDevInfo(DeviceInfo *device_info);
+
+/**
+ * @brief Get device info
+ *
+ * @param[in] device_info @see DeviceInfo
+ * @return @see IotReturnCode
+ */
+int HAL_GetDevInfo(DeviceInfo *device_info);
+
+/**************************************************************************************
+ * timer
+ **************************************************************************************/
+
+/**
+ * @brief time format string
+ *
+ * @return time format string, such as "2021-05-31 15:58:46"
+ */
+char *HAL_Timer_Current(void);
+
+/**
+ * @brief Get utc time ms timestamp.
+ *
+ * @return timestamp
+ */
+uint64_t HAL_Timer_CurrentMs(void);
+
+/**
+ * @brief Set system time using second timestamp
+ *
+ * @param[in] timestamp_ms
+ * @return 0 for success
+ */
+int HAL_Timer_SetSystimeMs(uint64_t timestamp_ms);
+
+/**************************************************************************************
+ * network tcp
+ **************************************************************************************/
+
+/**
+ * @brief TCP connect in linux
+ *
+ * @param[in] host host to connect
+ * @param[out] port port to connect
+ * @return socket fd
+ */
+int HAL_TCP_Connect(const char *host, const char *port);
+
+/**
+ * @brief TCP disconnect
+ *
+ * @param[in] fd socket fd
+ * @return 0 for success
+ */
+int HAL_TCP_Disconnect(int fd);
+
+/**
+ * @brief TCP write
+ *
+ * @param[in] fd socket fd
+ * @param[in] buf buf to write
+ * @param[in] len buf len
+ * @param[in] timeout_ms timeout
+ * @param[out] written_len data written length
+ * @return @see IotReturnCode
+ */
+int HAL_TCP_Write(int fd, const uint8_t *data, uint32_t len, uint32_t timeout_ms, size_t *written_len);
+
+/**
+ * @brief TCP read.
+ *
+ * @param[in] fd socket fd
+ * @param[out] buf buffer to save read data
+ * @param[in] len buffer len
+ * @param[in] timeout_ms timeout
+ * @param[out] read_len length of data read
+ * @return @see IotReturnCode
+ */
+int HAL_TCP_Read(int fd, uint8_t *data, uint32_t len, uint32_t timeout_ms, size_t *read_len);
+
+/**************************************************************************************
+ * AT module
+ **************************************************************************************/
+
+#ifdef AT_MODULE_ENABLE
+
+/**
+ * @brief Urc handler.
+ *
+ */
+typedef void (*OnUrcHandler)(const char *data, size_t data_len);
+
+/**
+ * @brief Init at module.
+ *
+ * @return 0 for success
+ */
+int HAL_Module_Init(void);
+
+/**
+ * @brief Deinit at module.
+ *
+ */
+void HAL_Module_Deinit(void);
+
+/**
+ * @brief Send at cmd to at module and wait for resp.
+ *
+ * @param[in] at_cmd at cmd
+ * @param[in] at_expect expect resp
+ * @param[in] timeout_ms wait timeout
+ * @return 0 for success
+ */
+int HAL_Module_SendAtCmdWaitResp(const char *at_cmd, const char *at_expect, uint32_t timeout_ms);
+
+/**
+ * @brief Send at cmd and waif for data.
+ *
+ * @param[in] at_cmd at cmd
+ * @param[in] at_expect expect resp
+ * @param[out] recv_buf recv data buffer
+ * @param[out] recv_len recv data length
+ * @param[in] timeout_ms wait timeout
+ * @return 0 for success
+ */
+int HAL_Module_SendAtCmdWaitRespWithData(const char *at_cmd, const char *at_expect, void *recv_buf, uint32_t *recv_len,
+ uint32_t timeout_ms);
+
+/**
+ * @brief Send date to at module.
+ *
+ * @param[in] data data to send
+ * @param[in] data_len data length
+ * @return 0 for success
+ */
+int HAL_Module_SendAtData(const void *data, int data_len);
+
+/**
+ * @brief Set urc.
+ *
+ * @param[in] urc irc string
+ * @param[in] urc_handler urc handler
+ * @return 0 for success
+ */
+int HAL_Module_SetUrc(const char *urc, OnUrcHandler urc_handler);
+
+/**
+ * @brief connect network
+ *
+ * @return int 0 for success
+ */
+int HAL_Module_ConnectNetwork(void);
+
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_PLATFORM_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/common/qcloud_iot_timer.h b/components/connectivity/iot-hub-device-c-sdk/include/common/qcloud_iot_timer.h
new file mode 100644
index 00000000..9ada2639
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/common/qcloud_iot_timer.h
@@ -0,0 +1,88 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_timer.h
+ * @brief timer interface
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-04-07
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-04-07 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_TIMER_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_TIMER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "qcloud_iot_platform.h"
+
+/**
+ * @brief Define timer.
+ *
+ */
+#define QcloudIotTimer uint64_t
+
+/**
+ * @brief Return if timer expired.
+ *
+ * @param[in] timer @see QcloudIotTimer
+ * @return true expired
+ * @return false no expired
+ */
+bool IOT_Timer_Expired(QcloudIotTimer *timer);
+
+/**
+ * @brief Countdown ms.
+ *
+ * @param[in,out] timer @see QcloudIotTimer
+ * @param[in] timeout_ms ms to count down
+ */
+void IOT_Timer_CountdownMs(QcloudIotTimer *timer, uint32_t timeout_ms);
+
+/**
+ * @brief Countdown second
+ *
+ * @param[in,out] timer @see QcloudIotTimer
+ * @param[in] timeout second to count down
+ */
+void IOT_Timer_Countdown(QcloudIotTimer *timer, uint32_t timeout);
+
+/**
+ * @brief QcloudIotTimer remain ms.
+ *
+ * @param[in] timer @see QcloudIotTimer
+ * @return ms
+ */
+uint64_t IOT_Timer_Remain(QcloudIotTimer *timer);
+
+/**
+ * @brief Get current utf timestamp of second
+ *
+ * @return timestamp
+ */
+uint64_t IOT_Timer_CurrentSec(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_TIMER_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/config/qcloud_iot_config.h b/components/connectivity/iot-hub-device-c-sdk/include/config/qcloud_iot_config.h
new file mode 100644
index 00000000..f52c60e5
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/config/qcloud_iot_config.h
@@ -0,0 +1,55 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_config.h
+ * @brief sdk config define
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-06-01
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-06-01 1.0 fancyxu first commit
+ * 2021-07-12 1.1 fancyxu rename AUTH_WITH_NOTLS to AUTH_WITH_NO_TLS
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_CONFIG_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_CONFIG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* #undef AUTH_MODE_CERT */
+#define AUTH_MODE_KEY
+#define AUTH_WITH_NO_TLS
+/* #undef GATEWAY_ENABLED */
+/* #undef SYSTEM_COMM */
+/* #undef DEV_DYN_REG_ENABLED */
+/* #undef LOG_UPLOAD */
+/* #undef IOT_DEBUG */
+#define DEBUG_DEV_INFO_USED
+#define AT_MODULE_ENABLE
+#define MULTITHREAD_ENABLED
+/* #undef LOG_UPLOAD_TYPE_JSON */
+/* #undef LOG_UPLOAD_AES_ENCRYPT_POST */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_CONFIG_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/config/qcloud_iot_host.h b/components/connectivity/iot-hub-device-c-sdk/include/config/qcloud_iot_host.h
new file mode 100644
index 00000000..45020a15
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/config/qcloud_iot_host.h
@@ -0,0 +1,87 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_host.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-28
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-28 1.0 fancyxu first commit
+ * 2021-07-12 1.1 fancyxu change port to str format
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_HOST_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_HOST_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief IoT C-SDK APPID.
+ *
+ */
+#define QCLOUD_IOT_DEVICE_SDK_APPID "21010406"
+#define QCLOUD_IOT_DEVICE_SDK_APPID_LEN (sizeof(QCLOUD_IOT_DEVICE_SDK_APPID) - 1)
+
+/**
+ * @brief MQTT server domain.
+ *
+ */
+#define QCLOUD_IOT_MQTT_DIRECT_DOMAIN "iotcloud.tencentdevices.com"
+#define MQTT_SERVER_PORT_TLS "8883"
+#define MQTT_SERVER_PORT_NO_TLS "1883"
+
+/**
+ * @brief Server domain for dynamic registering device.
+ *
+ */
+#define DYN_REG_SERVER_URL "ap-guangzhou.gateway.tencentdevices.com"
+#define DYN_REG_URI_PATH "/device/register"
+#define DYN_REG_SERVER_PORT "80"
+#define DYN_REG_SERVER_PORT_TLS "443"
+
+/**
+ * @brief URL for doing log upload.
+ *
+ */
+#define LOG_UPLOAD_SERVER_URL "ap-guangzhou.gateway.tencentdevices.com"
+#define LOG_UPLOAD_URI_PATH "/device/reportlog"
+#define LOG_UPLOAD_SERVER_PORT "80"
+
+/**
+ * @brief (old)URL for doing log upload.
+ * Compatible with old versions, new users are recommended to use up
+ *
+ */
+#define LOG_UPLOAD_OLD_SERVER_URL "http://devicelog.iot.cloud.tencent.com/cgi-bin/report-log"
+#define LOG_UPLOAD_OLD_SERVER_PORT "80"
+
+/**
+ * @brief Max size of a host name.
+ *
+ */
+#define HOST_STR_LENGTH 64
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_HOST_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/config/qcloud_iot_variables.h b/components/connectivity/iot-hub-device-c-sdk/include/config/qcloud_iot_variables.h
new file mode 100644
index 00000000..4c9fa433
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/config/qcloud_iot_variables.h
@@ -0,0 +1,119 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_variables.h
+ * @brief variables can be set depend on user resource.
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-28
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-28 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_VARIABLES_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_VARIABLES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Variables are dependant on user situation (network status/device memory/application context). Adjust the default
+// value to meet your requirement
+
+/**
+ * @brief default MQTT timeout value when connect/pub/sub (unit: ms)
+ *
+ */
+#define QCLOUD_IOT_MQTT_COMMAND_TIMEOUT (5 * 1000)
+
+/**
+ * @brief default MQTT timeout value when wait server ack
+ *
+ */
+#define QCLOUD_IOT_MQTT_WAIT_ACK_TIMEOUT (5 * 1000)
+
+/**
+ * @brief default MQTT keep alive interval (unit: s)
+ *
+ */
+#define QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL (240)
+
+/**
+ * @brief default MQTT Tx buffer size, MAX: 16*1024
+ *
+ */
+#define QCLOUD_IOT_MQTT_TX_BUF_LEN (4096)
+
+/**
+ * @brief default MQTT Rx buffer size, MAX: 16*1024
+ *
+ */
+#define QCLOUD_IOT_MQTT_RX_BUF_LEN (4096)
+
+/**
+ * @brief default MQTT timeout value when yield
+ *
+ */
+#define QCLOUD_IOT_MQTT_YIELD_TIMEOUT (500)
+
+/**
+ * @brief MAX MQTT reconnect interval (unit: ms)
+ *
+ */
+#define MAX_RECONNECT_WAIT_INTERVAL (60 * 1000)
+
+/**
+ * @brief MAX valid time when connect to MQTT server. 0: always valid.
+ * Use this only if the device has accurate UTC time. Otherwise, set to 0.
+ *
+ */
+#define MAX_ACCESS_EXPIRE_TIMEOUT (0)
+
+// Log upload related params, which will affect the size of device memory/disk consumption Log upload related params,
+// which will affect the size of device memory/disk consumption
+
+/**
+ * @brief size of buffer for log upload
+ *
+ */
+#define LOG_UPLOAD_BUFFER_SIZE 5000
+
+/**
+ * @brief Max size of one http log upload. Should not larger than 5000
+ *
+ */
+#define MAX_HTTP_LOG_POST_SIZE 5000
+
+/**
+ * @brief MAX size for saving log into NVS (files/FLASH) after upload fail
+ *
+ */
+#define MAX_LOG_SAVE_SIZE (3 * LOG_UPLOAD_BUFFER_SIZE)
+
+/**
+ * @brief interval of log upload (unit: ms) Decrease this value if LOG_UPLOAD_BUFFER_SIZE is small
+ *
+ */
+#define LOG_UPLOAD_INTERVAL_MS 1800000
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_VARIABLES_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/qcloud_iot_common.h b/components/connectivity/iot-hub-device-c-sdk/include/qcloud_iot_common.h
new file mode 100644
index 00000000..167f7924
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/qcloud_iot_common.h
@@ -0,0 +1,70 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_common.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-08-23
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-08-23 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_QCLOUD_IOT_COMMON_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_QCLOUD_IOT_COMMON_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief IoT C-SDK version info
+ *
+ */
+#define QCLOUD_IOT_DEVICE_SDK_VERSION "4.0.0"
+
+// common header file
+#include "qcloud_iot_debug.h"
+#include "qcloud_iot_device.h"
+#include "qcloud_iot_error.h"
+#include "qcloud_iot_params_check.h"
+#include "qcloud_iot_platform.h"
+#include "qcloud_iot_timer.h"
+
+// config header file
+#include "qcloud_iot_config.h"
+#include "qcloud_iot_host.h"
+#include "qcloud_iot_variables.h"
+
+// service header file
+#include "qcloud_iot_mqtt_client.h"
+#include "qcloud_iot_system.h"
+#include "qcloud_iot_ota.h"
+#include "qcloud_iot_cos.h"
+#include "qcloud_iot_http_client.h"
+#include "qcloud_iot_log_upload.h"
+#include "qcloud_iot_http_signed.h"
+#include "qcloud_iot_dynreg.h"
+#include "qcloud_iot_gateway.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_QCLOUD_IOT_COMMON_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/qcloud_iot_explorer.h b/components/connectivity/iot-hub-device-c-sdk/include/qcloud_iot_explorer.h
new file mode 100644
index 00000000..77089648
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/qcloud_iot_explorer.h
@@ -0,0 +1,51 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_explorer.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-08-23
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-08-23 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_QCLOUD_IOT_EXPLORER_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_QCLOUD_IOT_EXPLORER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief IoT C-SDK version info
+ *
+ */
+#define QCLOUD_IOT_DEVICE_EXPLORER_SDK_VERSION "4.0.0"
+
+// service header file
+#include "qcloud_iot_data_template.h"
+#include "qcloud_iot_file_manage.h"
+#include "qcloud_iot_gateway_scene.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_QCLOUD_IOT_EXPLORER_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/qcloud_iot_hub.h b/components/connectivity/iot-hub-device-c-sdk/include/qcloud_iot_hub.h
new file mode 100644
index 00000000..1b8e3d4f
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/qcloud_iot_hub.h
@@ -0,0 +1,50 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_hub.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-28
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-28 1.0 fancyxu first commit
+ * 2021-07-18 1.1 fancyxu support broadcast
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_QCLOUD_IOT_HUB_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_QCLOUD_IOT_HUB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief IoT C-SDK version info
+ *
+ */
+#define QCLOUD_IOT_DEVICE_HUB_SDK_VERSION "4.0.0"
+
+// service header file
+#include "qcloud_iot_broadcast.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_QCLOUD_IOT_HUB_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_cos.h b/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_cos.h
new file mode 100644
index 00000000..0b196773
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_cos.h
@@ -0,0 +1,90 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_cos.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-10-26
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-10-26 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_COS_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_COS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+#include "qcloud_iot_common.h"
+
+/**
+ * @brief Cos download params.
+ *
+ */
+typedef struct {
+ const char *url; /**< cos url, user space */
+ uint32_t offset; /**< download offset */
+ uint32_t file_size; /**< download file size */
+ int is_fragmentation; /**< http fragmentation support */
+ int is_https_enabled; /**< TODO:https support */
+} IotCosDownloadParams;
+
+/**
+ * @brief Init cos download handle.
+ *
+ * @param[in] params @see IotCosDownloadParams
+ * @return pointer to cos download handle
+ */
+void *IOT_COS_DownloadInit(IotCosDownloadParams *params);
+
+/**
+ * @brief Fetch data from cos.
+ *
+ * @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
+ * @param[out] buf buffer to store data
+ * @param[in] buf_len buffer length
+ * @param timeout_ms timeout for fetching
+ * @return >= 0 for recv data len. others @see IotReturnCode
+ */
+int IOT_COS_DownloadFetch(void *handle, uint8_t *buf, uint32_t buf_len, uint32_t timeout_ms);
+
+/**
+ * @brief Is download finished.
+ *
+ * @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
+ * @return true for finished
+ */
+int IOT_COS_DownloadIsFinished(void *handle);
+
+/**
+ * @brief Deinit cos download.
+ *
+ * @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
+ */
+void IOT_COS_DownloadDeinit(void *handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_COS_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_dynreg.h b/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_dynreg.h
new file mode 100644
index 00000000..738e9e0b
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_dynreg.h
@@ -0,0 +1,52 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_cos.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-10-26
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-10-26 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_DYNREG_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_DYNREG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+#include "qcloud_iot_common.h"
+
+/**
+ * @brief dynreg device, get device secret or device cert file and private key file from iot platform
+ *
+ * @param[in] params @see DeviceInfo
+ * @return 0 is success other is failed
+ */
+int IOT_DynReg_Device(DeviceInfo *device_info);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_DYNREG_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_gateway.h b/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_gateway.h
new file mode 100644
index 00000000..cf366bd3
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_gateway.h
@@ -0,0 +1,151 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_gateway.h
+ * @brief
+ * @author willssong (willssong@tencent.com)
+ * @version 1.0
+ * @date 2022-01-23
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-01-23 1.0 willssong first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_GATEWAY_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_GATEWAY_H_
+
+#include "qcloud_iot_common.h"
+
+/**
+ * @brief Gateway reply result code.
+ * @ref https://cloud.tencent.com/document/product/1081/47442#.E9.94.99.E8.AF.AF.E7.A0.81
+ *
+ */
+typedef enum {
+ IOT_GATEWAY_RET_SUCCESS = 0,
+ IOT_GATEWAY_ERR_UNBIND = -1,
+ IOT_GATEWAY_ERR_FAIL = -2,
+ IOT_GATEWAY_ERR_PARAM = 801,
+ IOT_GATEWAY_ERR_SUBDEV_INVALID = 802,
+ IOT_GATEWAY_ERR_SIGN_FAIL = 803,
+ IOT_GATEWAY_ERR_SIGN_METHOD_UNSUPPORTED = 804,
+ IOT_GATEWAY_ERR_SIGN_EXPIRED = 805,
+ IOT_GATEWAY_ERR_SUBDEV_BIND_ALREADY = 806,
+ IOT_GATEWAY_ERR_SUBDEV_TYPE_NORMAL = 807,
+ IOT_GATEWAY_ERR_OPERATION_UNSUPPORTED = 808,
+ IOT_GATEWAY_ERR_BIND_REPEAT = 809,
+ IOT_GATEWAY_ERR_SUBDEV_UNSUPPORTED = 810
+} IotGatewayResult;
+
+/**
+ * @brief Callback of gateway.
+ *
+ */
+typedef struct {
+ void (*bind_unbind_reply_callback)(UtilsJsonValue sub_devices, bool is_bind, void *usr_data);
+ void (*unbind_all_callback)(void *usr_data);
+ void (*online_offline_reply_callback)(UtilsJsonValue sub_devices, bool is_online, void *usr_data);
+ void (*search_device_callback)(bool is_on, void *usr_data);
+ void (*describe_subdevices_reply_callback)(UtilsJsonValue sub_devices, void *usr_data);
+ void (*change_subdevices_status_callback)(UtilsJsonValue sub_devices, bool is_bind, void *usr_data);
+} IotGatewayMessageCallback;
+
+/**
+ * @brief Subscribe gateway topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] callback @see IotGatewayMessageCallback
+ * @param[in] usr_data usr data using in callback
+ * @return @see IotReturnCode
+ */
+int IOT_Gateway_Init(void *client, IotGatewayMessageCallback callback, void *usr_data);
+
+/**
+ * @brief Unsubscribe gateway topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ */
+void IOT_Gateway_Deinit(void *client);
+
+/**
+ * @brief Get gateway init usr data.
+ *
+ * @param [in,out] client pointer to mqtt client
+ * @return usr data or NULL
+ */
+void *IOT_Gateway_GetUsrData(void *client);
+
+/**
+ * @brief Publish subdevice online/offline message. @ref https://cloud.tencent.com/document/product/1081/47442
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] buf buffer to hold publish message
+ * @param[in] buf_len buffer length
+ * @param[in] sub_dev_list subdevice list
+ * @param[in] num num of subdevice, @note only one subdevice is supported now.
+ * @param[in] is_online 1: online; 0: offline
+ * @return @see packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_Gateway_SubOnOffLine(void *client, char *buf, int buf_len, const DeviceInfo *sub_dev_list[], int num,
+ bool is_online);
+
+/**
+ * @brief Publish subdevice bind/unbind message. @ref https://cloud.tencent.com/document/product/1081/47441
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] buf buffer to hold publish message
+ * @param[in] buf_len buffer length
+ * @param[in] sub_dev_list subdevice list
+ * @param[in] num num of subdevice, @note only one subdevice is supported now.
+ * @param[in] is_bind 1: bind; 0: unbind
+ * @return @see packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_Gateway_BindUnbind(void *client, char *buf, int buf_len, const DeviceInfo *sub_dev_list[], int num,
+ bool is_bind);
+
+/**
+ * @brief Publish subdevice describe message.
+ * @ref https://cloud.tencent.com/document/product/1081/47441#.E6.9F.A5.E8.AF.A2.E6.8B.93.E6.89.91.E5.85.B3.E7.B3.BB
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return @see packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_Gateway_Describe(void *client);
+
+/**
+ * @brief Publish search device reply message.
+ * @ref
+ * https://cloud.tencent.com/document/product/1081/47441#.E9.80.9A.E7.9F.A5.E7.BD.91.E5.85.B3.E5.BC.80.E5.90.AF.E6.90.9C.E7.B4.A2.E7.8A.B6.E6.80.81
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] is_on 1: on; 0: off
+ * @param[in] result 0: success; 1: fail
+ * @return @see packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_Gateway_SearchDeviceReply(void *client, bool is_on, int result);
+
+/**
+ * @brief Publish unbind all reply message.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] result 0: success; 1: fail
+ * @return @see packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_Gateway_UnbindAllReply(void *client, int result);
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_GATEWAY_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_http_client.h b/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_http_client.h
new file mode 100644
index 00000000..67a99e0c
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_http_client.h
@@ -0,0 +1,157 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_http_client.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-10-25
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-10-25 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_HTTP_CLIENT_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_HTTP_CLIENT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "qcloud_iot_common.h"
+
+/**
+ * @brief HTTP write socket timeout.
+ *
+ */
+#define HTTP_WRITE_TIMEOUT_MS 5000
+
+/**
+ * @brief HTTPs read tls timeout.
+ *
+ */
+#define HTTPS_READ_TIMEOUT_MS 2000
+
+/**
+ * @brief Connect params set by user.
+ *
+ */
+typedef struct {
+ const char *url; /**< http header */
+ const char *port; /**< content length(optional) */
+ const char *ca_crt; /**< content type(optional) */
+} IotHTTPConnectParams;
+
+/**
+ * @brief Http method.
+ *
+ */
+typedef enum {
+ IOT_HTTP_METHOD_GET = 0,
+ IOT_HTTP_METHOD_POST,
+ IOT_HTTP_METHOD_PUT,
+ IOT_HTTP_METHOD_DELETE,
+ IOT_HTTP_METHOD_HEAD,
+} IotHTTPMethod;
+
+/**
+ * @brief Request params set by user.
+ * @ref https://datatracker.ietf.org/doc/html/rfc7231
+ *
+ */
+typedef struct {
+ const char *url; /**< request url */
+ IotHTTPMethod method; /**< request method */
+ char *header; /**< http header */
+ int content_length; /**< content length(optional) */
+ char *content_type; /**< content type(optional) */
+ char *content; /**< content (optional) */
+} IotHTTPRequestParams;
+
+/**
+ * @brief Malloc http client.
+ *
+ * @return pointer to http client
+ */
+void *IOT_HTTP_Init(void);
+
+/**
+ * @brief Free http client.
+ *
+ */
+void IOT_HTTP_Deinit(void *client);
+
+/**
+ * @brief Connect http server.
+ *
+ * @param[in,out] client pointer to http client
+ * @param[in] params params needed to connect http server, @see IotHTTPConnectParams
+ * @return 0 for success. others @see IotReturnCode
+ */
+int IOT_HTTP_Connect(void *client, IotHTTPConnectParams *params);
+
+/**
+ * @brief Request http server.
+ *
+ * @param[in,out] client pointer to http client
+ * @param[in] params params needed to send request to http server, @see IotHTTPRequestParams
+ * @return 0 for success. others @see IotReturnCode
+ */
+int IOT_HTTP_Request(void *client, IotHTTPRequestParams *params);
+
+/**
+ * @brief Send data to http server.
+ *
+ * @param[in,out] client pointer to http client
+ * @param[in] data data to send
+ * @param[out] data_len data len
+ * @return 0 for success. others @see IotReturnCode
+ */
+int IOT_HTTP_Send(void *client, uint8_t *data, int data_len);
+
+/**
+ * @brief Recv data from http server.
+ *
+ * @param[in,out] client pointer to http client
+ * @param[out] buf buffer to store recv data
+ * @param[in] buf_len buffer len
+ * @param timeout_ms timeout for recv
+ * @return >= 0 for recv data len. others @see IotReturnCode
+ */
+int IOT_HTTP_Recv(void *client, uint8_t *buf, int buf_len, uint32_t timeout_ms);
+
+/**
+ * @brief Check is recv finished.
+ *
+ * @param[in,out] client pointer to http client
+ * @return true for finished.
+ */
+int IOT_HTTP_IsRecvFinished(void *client);
+
+/**
+ * @brief Disconnect http server.
+ *
+ * @param[in,out] client pointer to http client
+ */
+void IOT_HTTP_Disconnect(void *client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_HTTP_CLIENT_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_http_signed.h b/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_http_signed.h
new file mode 100644
index 00000000..b0fc7741
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_http_signed.h
@@ -0,0 +1,61 @@
+/**
+ * @file qcloud_iot_http_signed.h
+ * @author {hubert} ({hubertxxu@tencent.com})
+ * @brief
+ * @version 1.0
+ * @date 2022-01-17
+ *
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-01-17 1.0 hubertxxu first commit
+ *
+ */
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_HTTP_SIGNED_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_HTTP_SIGNED_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "qcloud_iot_common.h"
+
+typedef struct {
+ const char *host;
+ const char *uri;
+ char *secretkey;
+ uint32_t recv_timeout_ms;
+ bool need_recv;
+} HttpSignedParams;
+
+/**
+ * @brief post message and recv response
+ *
+ * @param params @see HttpSignedParams
+ * @param request_buf request buffer
+ * @param request_buf_len request buffer length
+ * @param response_buf response buffer if need recv
+ * @param response_buf_len response buffer length if need recv
+ * @return int 0 for success. others @see IotReturnCode
+ */
+int IOT_HTTP_SignedRequest(HttpSignedParams *params, const char *request_buf, size_t request_buf_len,
+ uint8_t *response_buf, int response_buf_len);
+
+#ifdef __cplusplus
+}
+#endif
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_HTTP_SIGNED_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_log_upload.h b/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_log_upload.h
new file mode 100644
index 00000000..6b001926
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_log_upload.h
@@ -0,0 +1,128 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_log_upload.h
+ * @brief
+ * @author hubertxxu (hubertxxu@tencent.com)
+ * @version 1.0
+ * @date 2022-01-05
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-01-05 1.0 hubertxxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_LOG_UPLOAD_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_LOG_UPLOAD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+#include "qcloud_iot_common.h"
+
+#include "utils_json.h"
+
+/**
+ * @brief User callback for saving/reading logs into/from NVS(files/FLASH) after upload fail/recover
+ *
+ */
+// callback for saving logs into NVS(files/FLASH) after upload fail
+typedef size_t (*LogSaveFunc)(const char *filename, const void *buf, size_t write_len, size_t offset);
+// callback for reading logs from NVS(files/FLASH) when upload ready
+typedef size_t (*LogReadFunc)(const char *filename, void *buf, size_t read_len, size_t offset);
+// callback for deleting logs in NVS(files/FLASH). return 0 when success
+typedef int (*LogDelFunc)(const char *filename);
+// callback for reading the size of logs in NVS(files/FLASH). return 0 when nothing exist
+typedef size_t (*LogGetSizeFunc)(const char *filename);
+
+/**
+ * @brief Data structure to init feature of log upload.
+ *
+ */
+typedef struct {
+ /* device info */
+ const char *product_id;
+ const char *device_name;
+ /* auth key, use device secret for PSK device and cert file path for cert device */
+ const char *sign_key;
+ /* http connect domain */
+ const char *host;
+ uint32_t log_buffer_size;
+ /* user callback saving/reading logs into/from NVS(files/FLASH) */
+ const char *save_log_filename;
+ LogSaveFunc save_func;
+ LogReadFunc read_func;
+ LogDelFunc del_func;
+ LogGetSizeFunc get_size_func;
+} LogUploadInitParams;
+
+/**
+ * @brief Default params.
+ *
+ */
+#define DEFAULT_LOG_UPLOAD_INIT_PARAMS \
+ { \
+ NULL, NULL, NULL, NULL, LOG_UPLOAD_BUFFER_SIZE, NULL, NULL, NULL, NULL, NULL \
+ }
+
+/**
+ * @brief Init log upload previously.
+ *
+ * @param[in] init_params @see LogUploadInitParams
+ * @return @see IotReturnCode
+ */
+int IOT_Log_Upload_InitPre(const LogUploadInitParams *init_params);
+
+/**
+ * @brief Init log upload module.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return @see IotReturnCode
+ */
+int IOT_Log_Upload_Init(void *client);
+
+/**
+ * @brief Stop log upload add release resources.
+ *
+ * @return @see IotReturnCode
+ */
+int IOT_Log_Upload_Deinit(void);
+
+/**
+ * @brief Append need report log to log upload buffer.
+ *
+ * @param[in] log_level @see LogLevel
+ * @param[in] log_content data of need to report
+ */
+void IOT_Log_Upload_AppendToUploadBuffer(LogLevel log_level, const char *log_content);
+
+/**
+ * @brief Do log upload.
+ *
+ * @param[in] force_upload force upload when error
+ * @return @see IotReturnCode
+ */
+int IOT_Log_Upload(bool force_upload);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_LOG_UPLOAD_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_mqtt_client.h b/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_mqtt_client.h
new file mode 100644
index 00000000..dcb690cc
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_mqtt_client.h
@@ -0,0 +1,339 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_mqtt_client.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-28
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-28 1.0 fancyxu first commit
+ * 2021-07-07 1.1 fancyxu support user host for unittest
+ * 2021-07-08 1.1 fancyxu fix code standard of IotReturnCode and QcloudIotClient
+ * 2021-07-18 1.1 fancyxu fix code standard of pClient
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_MQTT_CLIENT_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_MQTT_CLIENT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#include
+
+#include "qcloud_iot_common.h"
+
+/**
+ * @brief Max size of a topic name
+ *
+ */
+#define MAX_SIZE_OF_CLOUD_TOPIC ((MAX_SIZE_OF_DEVICE_NAME) + (MAX_SIZE_OF_PRODUCT_ID) + 64 + 6)
+
+/**
+ * @brief MQTT event type
+ *
+ */
+typedef enum {
+ MQTT_EVENT_UNDEF = 0, /**< MQTT undefined event */
+ MQTT_EVENT_DISCONNECT = 1, /**< MQTT disconnect */
+ MQTT_EVENT_RECONNECT = 2, /**< MQTT reconnect */
+ MQTT_EVENT_SUBSCRIBE_SUCCESS = 3, /**< MQTT subscribe success */
+ MQTT_EVENT_SUBSCRIBE_TIMEOUT = 4, /**< MQTT subscribe timeout */
+ MQTT_EVENT_SUBSCRIBE_NACK = 5, /**< MQTT subscribe fail */
+ MQTT_EVENT_UNSUBSCRIBE_SUCCESS = 6, /**< MQTT unsubscribe success */
+ MQTT_EVENT_UNSUBSCRIBE_TIMEOUT = 7, /**< MQTT unsubscribe timeout */
+ MQTT_EVENT_UNSUBSCRIBE_NACK = 8, /**< MQTT unsubscribe fail */
+ MQTT_EVENT_PUBLISH_SUCCESS = 9, /**< MQTT publish success */
+ MQTT_EVENT_PUBLISH_TIMEOUT = 10, /**< MQTT publish timeout */
+ MQTT_EVENT_PUBLISH_NACK = 11, /**< MQTT publish fail */
+ MQTT_EVENT_PUBLISH_RECEIVED = 12, /**< MQTT received msg from server */
+ MQTT_EVENT_CLIENT_DESTROY = 13, /**< MQTT client destroy */
+ MQTT_EVENT_UNSUBSCRIBE = 14, /**< MQTT unsubscribe */
+} MQTTEventType;
+
+/**
+ * @brief MQTT event message
+ *
+ */
+typedef struct {
+ MQTTEventType event_type; /**< MQTT event type */
+ void *msg;
+} MQTTEventMsg;
+
+/**
+ * @brief Define MQTT callback when MQTT event happen
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] context user callback context, @see MQTTEventHandler
+ * @param[in] msg the event message @see MQTTEventMsg
+ */
+typedef void (*MQTTEventHandleFun)(void *client, void *context, MQTTEventMsg *msg);
+
+/* The structure of MQTT event handle */
+
+/**
+ * @brief Define structure to handle mqtt event
+ *
+ */
+typedef struct {
+ MQTTEventHandleFun h_fp;
+ void *context;
+} MQTTEventHandler;
+
+/**
+ * @brief return next host ip
+ */
+typedef const char *(*MQTTGetNextHostIp)(void);
+
+/**
+ * @brief The structure of MQTT init parameters
+ *
+ */
+typedef struct {
+ DeviceInfo *device_info; /**< device info */
+ const char *host; /**< host for user, null for default using QCLOUD_IOT_MQTT_DIRECT_DOMAIN */
+ const char *backup_host; /**< backup host for user if host not connect will try use this */
+ uint32_t command_timeout; /**< timeout value (unit: ms) for MQTT connect/pub/sub/yield */
+ uint32_t keep_alive_interval; /**< MQTT keep alive time interval in second */
+ uint8_t clean_session; /**< flag of clean session, 1 clean, 0 not clean */
+ uint8_t auto_connect_enable; /**< flag of auto reconnection, 1 is enable and recommended */
+ uint8_t connect_when_construct; /**< 1 is enable when no using pre-process before connect */
+ uint8_t default_subscribe; /**< 1 is enable when clean session is 0, no subscribe packet send, only add subhandle */
+ MQTTGetNextHostIp get_next_host_ip; /**< get host ip*/
+ MQTTEventHandler event_handle; /**< event callback */
+} MQTTInitParams;
+
+/**
+ * Default MQTT init parameters
+ */
+#define DEFAULT_MQTT_INIT_PARAMS \
+ { \
+ NULL, NULL, NULL, QCLOUD_IOT_MQTT_COMMAND_TIMEOUT, QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL, 1, 1, 1, 0, NULL, \
+ { \
+ 0 \
+ } \
+ }
+
+/**
+ * @brief MQTT Quality of Service level
+ *
+ */
+typedef enum {
+ QOS0 = 0, /**< At most once delivery */
+ QOS1 = 1, /**< At least once delivery, PUBACK is required */
+ QOS2 = 2 /**< Exactly once delivery. NOT supported currently */
+} QoS;
+
+/**
+ * @brief MQTT message parameter for pub/sub
+ *
+ */
+typedef struct {
+ QoS qos; // MQTT QoS level
+ uint8_t retain; // RETAIN flag
+ uint8_t dup; // DUP flag
+ uint16_t packet_id; // MQTT Id
+ char *topic_name; // MQTT topic
+ int topic_len; // topic length
+ union {
+ uint8_t *payload; // MQTT msg payload
+ char *payload_str; // MQTT msg payload string
+ };
+ int payload_len; // MQTT length of msg payload
+} MQTTMessage;
+
+/**
+ * @brief Params needed to publish expcept topic name(as a paramter of function).
+ *
+ */
+typedef struct {
+ QoS qos; // MQTT QoS level
+ uint8_t retain; // RETAIN flag
+ uint8_t dup; // DUP flag
+ void *payload; // MQTT msg payload
+ int payload_len; // MQTT length of msg payload
+} PublishParams;
+
+/**
+ * @brief Default MQTT publish params
+ *
+ */
+#define DEFAULT_PUB_PARAMS \
+ { \
+ QOS0, 0, 0, NULL, 0 \
+ }
+
+/**
+ * @brief Define MQTT SUBSCRIBE callback when message arrived
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] message publish message from server
+ * @param[in] usr_data user data of SubscribeParams, @see SubscribeParams
+ */
+typedef void (*OnMessageHandler)(void *client, const MQTTMessage *message, void *usr_data);
+
+/**
+ * @brief Define MQTT SUBSCRIBE callback when event happened
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] event_type @see MQTTEventType
+ * @param[in] usr_data user data of SubscribeParams, @see SubscribeParams
+ *
+ * @return none
+ */
+typedef void (*OnSubEventHandler)(void *client, MQTTEventType event_type, void *usr_data);
+
+/**
+ * @brief Define structure to do MQTT subscription
+ *
+ */
+typedef struct {
+ QoS qos; /**< MQTT QoS level */
+ OnMessageHandler on_message_handler; /**< callback when message arrived */
+ OnSubEventHandler on_sub_event_handler; /**< callback when event happened */
+ void *user_data; /**< user context for callback */
+ void (*user_data_free)(void *); /**< user data free when sub handle remove */
+} SubscribeParams;
+
+/**
+ * Default MQTT subscribe parameters
+ *
+ */
+#define DEFAULT_SUB_PARAMS \
+ { \
+ QOS0, NULL, NULL, NULL, NULL \
+ }
+
+/**
+ * @brief Create MQTT client and connect to MQTT server.
+ *
+ * @param[in] params MQTT init parameters
+ * @return a valid MQTT client handle when success, or NULL otherwise
+ */
+void *IOT_MQTT_Construct(const MQTTInitParams *params);
+
+/**
+ * @brief Connect Mqtt server if not connect.
+ *
+ * @param[in,out] client pointer to mqtt client pointer, should using the pointer of IOT_MQTT_Construct return.
+ * @return @see IotReturnCode
+ */
+int IOT_MQTT_Connect(void *client);
+
+/**
+ * @brief Close connection and destroy MQTT client.
+ *
+ * @param client pointer to mqtt client pointer
+ * @return @see IotReturnCode
+ */
+int IOT_MQTT_Destroy(void **client);
+
+/**
+ * @brief Check connection and keep alive state, read/handle MQTT packet in synchronized way.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] timeout_ms timeout value (unit: ms) for this operation
+ * @return QCLOUD_RET_SUCCESS when success, QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT when try reconnecting, others @see
+ * IotReturnCode
+ */
+int IOT_MQTT_Yield(void *client, uint32_t timeout_ms);
+
+/**
+ * @brief Publish MQTT message.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_name topic to publish
+ * @param[in] params @see PublishParams
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_MQTT_Publish(void *client, const char *topic_name, const PublishParams *params);
+
+/**
+ * @brief Subscribe MQTT topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter to subscribe
+ * @param[in] params @see SubscribeParams
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_MQTT_Subscribe(void *client, const char *topic_filter, const SubscribeParams *params);
+
+/**
+ * @brief Unsubscribe MQTT topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter to unsubscribe
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_MQTT_Unsubscribe(void *client, const char *topic_filter);
+
+/**
+ * @brief Check if MQTT topic has been subscribed or not
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter to subscribe
+ * @return true already subscribed
+ * @return false not ready
+ */
+bool IOT_MQTT_IsSubReady(void *client, const char *topic_filter);
+
+/**
+ * @brief Get user data in subscribe.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter to subscribe
+ * @return NULL or user data
+ */
+void *IOT_MQTT_GetSubUsrData(void *client, const char *topic_filter);
+
+/**
+ * @brief Subscribe and wait sub ready.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter to subscribe
+ * @param[in] params @see SubscribeParams
+ * @return @see IotReturnCode
+ */
+int IOT_MQTT_SubscribeSync(void *client, const char *topic_filter, const SubscribeParams *params);
+
+/**
+ * @brief Check if MQTT is connected.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return true connected
+ * @return false no connected
+ */
+bool IOT_MQTT_IsConnected(void *client);
+
+/**
+ * @brief Get device info using to connect mqtt server.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return @see DeviceInfo
+ */
+DeviceInfo *IOT_MQTT_GetDeviceInfo(void *client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_MQTT_CLIENT_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_ota.h b/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_ota.h
new file mode 100644
index 00000000..a2d72900
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_ota.h
@@ -0,0 +1,154 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_ota.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-10-11
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-10-11 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_OTA_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_OTA_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+#include "qcloud_iot_common.h"
+
+#include "utils_json.h"
+
+#ifdef AT_MODULE_ENABLE
+
+/**
+ * @brief Init ota && report mcu & at version.
+ *
+ * @param[in] version mcu version.
+ * @return 0 for success
+ */
+int IOT_OTA_Init(const char *version);
+
+/**
+ * @brief Deinit ota.
+ *
+ * @return @see IotReturnCode
+ */
+void IOT_OTA_Deinit(void);
+
+/**
+ * @brief Read fw info from at module.
+ *
+ * @param[out] version mcu fw version
+ * @param[out] fw_size mcu fw size
+ * @param[out] md5 mcu fw md5
+ * @param[in] timeout_ms timeout
+ * @return 0 for success
+ */
+int IOT_OTA_ReadFwInfo(char **version, uint32_t *fw_size, char **md5, uint32_t timeout_ms);
+
+/**
+ * @brief Read fw data from at module.
+ *
+ * @param[out] fw_data fw data
+ * @param[out] fw_data_len fw data length
+ * @param[in] timeout_ms timeout
+ * @return 0 for success
+ */
+int IOT_OTA_ReadFWData(uint8_t *fw_data, uint32_t *fw_data_len, uint32_t timeout_ms);
+
+#else
+/**
+ * @brief OTA report type.
+ *
+ */
+typedef enum {
+ IOT_OTA_REPORT_TYPE_DOWNLOADING = 0,
+ IOT_OTA_REPORT_TYPE_UPGRADE_BEGIN,
+ IOT_OTA_REPORT_TYPE_UPGRADE_SUCCESS,
+ IOT_OTA_REPORT_TYPE_DOWNLOAD_TIMEOUT,
+ IOT_OTA_REPORT_TYPE_FILE_NOT_EXIST,
+ IOT_OTA_REPORT_TYPE_AUTH_FAIL,
+ IOT_OTA_REPORT_TYPE_MD5_NOT_MATCH,
+ IOT_OTA_REPORT_TYPE_UPGRADE_FAIL,
+} IotOTAReportType;
+
+/**
+ * @brief Callback of OTA.
+ *
+ */
+typedef struct {
+ void (*update_firmware_callback)(UtilsJsonValue version, UtilsJsonValue url, UtilsJsonValue md5sum,
+ uint32_t file_size, void *usr_data);
+ void (*report_version_reply_callback)(int result_code, void *usr_data);
+} IotOTAUpdateCallback;
+
+/**
+ * @brief OTA init, subscribe update topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] callback @see IotOTAUpdateCallback
+ * @param[in] usr_data usr data used in callback
+ * @return 0 for success, or err code (<0) @see IotReturnCode
+ */
+int IOT_OTA_Init(void *client, IotOTAUpdateCallback callback, void *usr_data);
+
+/**
+ * @brief OTA deinit, unsubscribe update topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ */
+void IOT_OTA_Deinit(void *client);
+
+/**
+ * @brief Report upgrade progress.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[out] buf publish message buffer
+ * @param[in] buf_len buffer len
+ * @param[in] report_type @see IotOTAReportType
+ * @param[in] progress progress using in IOT_OTA_REPORT_TYPE_DOWNLOADING
+ * @param[in] version update firmware version
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_OTA_ReportProgress(void *client, char *buf, int buf_len, IotOTAReportType report_type, int progress,
+ const char *version);
+
+/**
+ * @brief Report current firmware version.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[out] buf publish message buffer
+ * @param[in] buf_len buffer len
+ * @param[in] version current firmware version
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_OTA_ReportVersion(void *client, char *buf, int buf_len, const char *version);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_OTA_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_system.h b/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_system.h
new file mode 100644
index 00000000..1e0daaf9
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/services/common/qcloud_iot_system.h
@@ -0,0 +1,73 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_system.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-07-24
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-07-24 1.0 fancyxu first commit
+ * 2022-01-26 1.1 hubert add serverip
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_SYSTEM_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_SYSTEM_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+#include "qcloud_iot_common.h"
+
+#include "utils_json.h"
+
+/**
+ * @brief Get time from system topic
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[out] time time from system topic
+ * @return @see IotReturnCode
+ */
+int IOT_Sys_GetTime(void *client, uint32_t *time);
+
+/**
+ * @brief Get ntp time and set to system.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return @see IotReturnCode
+ */
+int IOT_Sys_SyncNTPTime(void *client);
+
+/**
+ * @brief Get serverip from system result topic
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[out] server_ip serverip from system result topic
+ * @return @see IotReturnCode
+ */
+int IOT_Sys_GetServerIp(void *client, char *server_ip);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_SYSTEM_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/services/explorer/qcloud_iot_data_template.h b/components/connectivity/iot-hub-device-c-sdk/include/services/explorer/qcloud_iot_data_template.h
new file mode 100644
index 00000000..7cfcd813
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/services/explorer/qcloud_iot_data_template.h
@@ -0,0 +1,214 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_data_template.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-08-23
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-08-23 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_EXPLORER_QCLOUD_IOT_DATA_TEMPLATE_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_EXPLORER_QCLOUD_IOT_DATA_TEMPLATE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#include
+
+#include "qcloud_iot_common.h"
+
+/**
+ * @brief Callback of property.
+ *
+ */
+typedef struct {
+ void (*method_control_callback)(UtilsJsonValue client_token, UtilsJsonValue params, void *usr_data);
+ void (*method_report_reply_callback)(UtilsJsonValue client_token, int code, void *usr_data);
+ void (*method_get_status_reply_callback)(UtilsJsonValue client_token, int code, UtilsJsonValue reported,
+ UtilsJsonValue control, void *usr_data);
+ void (*method_report_info_reply_callback)(UtilsJsonValue client_token, int code, void *usr_data);
+ void (*method_clear_control_reply_callback)(UtilsJsonValue client_token, int code, void *usr_data);
+} PropertyMessageCallback;
+
+/**
+ * @brief Callback of event.
+ *
+ */
+typedef struct {
+ void (*method_event_reply_callback)(UtilsJsonValue client_token, int code, void *usr_data);
+} EventMessageCallback;
+
+/**
+ * @brief Callback of action.
+ *
+ */
+typedef struct {
+ void (*method_action_callback)(UtilsJsonValue client_token, UtilsJsonValue action_id, UtilsJsonValue params,
+ void *usr_data);
+} ActionMessageCallback;
+
+/**
+ * @brief Callback of data template(including property/event/action).
+ *
+ */
+typedef struct {
+ PropertyMessageCallback property_callback;
+ EventMessageCallback event_callback;
+ ActionMessageCallback action_callback;
+} IotDataTemplateCallback;
+
+#define DEFAULT_DATA_TEMPLATE_CALLBACK \
+ { \
+ {NULL, NULL, NULL, NULL, NULL}, {NULL}, {NULL}, \
+ }
+
+/**
+ * @brief Type of event.
+ *
+ */
+typedef enum {
+ IOT_DATA_TEMPLATE_EVENT_TYPE_INFO = 0,
+ IOT_DATA_TEMPLATE_EVENT_TYPE_ALERT,
+ IOT_DATA_TEMPLATE_EVENT_TYPE_FAULT,
+} IotDataTemplateEventType;
+
+/**
+ * @brief Post data.
+ *
+ */
+typedef struct {
+ const char * event_id; /**< event id defined in data template */
+ IotDataTemplateEventType type; /**< event type defined in data template */
+ const char * params; /**< property json defined in data template */
+} IotDataTemplateEventData;
+
+/**
+ * @brief Action reply.
+ *
+ */
+typedef struct {
+ UtilsJsonValue client_token;
+ int code;
+ const char * response; /**< property json defined in data template */
+} IotDataTemplateActionReply;
+
+/**
+ * @brief Check and subscribe data template topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] callback @see IotDataTemplateCallback
+ * @param[in] usr_data usr data used in callback
+ * @return 0 for success, or err code (<0) @see IotReturnCode
+ */
+int IOT_DataTemplate_Init(void *client, IotDataTemplateCallback callback, void *usr_data);
+
+/**
+ * @brief Unsubscribe data template topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ */
+void IOT_DataTemplate_Deinit(void *client);
+
+/**
+ * @brief Report property.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] buf buffer for message
+ * @param[in] buf_len buffer length
+ * @param[in] params params constructed with property
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_DataTemplate_PropertyReport(void *client, char *buf, int buf_len, const char *params);
+
+/**
+ * @brief Get control message offline.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] buf buffer for message
+ * @param[in] buf_len buffer length
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_DataTemplate_PropertyGetStatus(void *client, char *buf, int buf_len);
+
+/**
+ * @brief Report device info.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] buf buffer for message
+ * @param[in] buf_len buffer length
+ * @param[in] params params constructed with device info
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_DataTemplate_PropertyReportInfo(void *client, char *buf, int buf_len, const char *params);
+
+/**
+ * @brief Clear control message offline.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] buf buffer for message
+ * @param[in] buf_len buffer length
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_DataTemplate_PropertyClearControl(void *client, char *buf, int buf_len);
+
+/**
+ * @brief Reply control message.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] buf buffer for message
+ * @param[in] buf_len buffer length
+ * @param[in] code 0 for success
+ * @param[in] client_token client token of control message
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_DataTemplate_PropertyControlReply(void *client, char *buf, int buf_len, int code, UtilsJsonValue client_token);
+
+/**
+ * @brief Post event.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] buf buffer for message
+ * @param[in] buf_len buffer length
+ * @param[in] data @see IotDataTemplateEventData
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_DataTemplate_EventPost(void *client, char *buf, int buf_len, IotDataTemplateEventData data);
+
+/**
+ * @brief Reply action message.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] buf buffer for message
+ * @param[in] buf_len buffer length
+ * @param[in] reply @see IotDataTemplateActionReply
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_DataTemplate_ActionReply(void *client, char *buf, int buf_len, IotDataTemplateActionReply reply);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_EXPLORER_QCLOUD_IOT_DATA_TEMPLATE_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/services/explorer/qcloud_iot_file_manage.h b/components/connectivity/iot-hub-device-c-sdk/include/services/explorer/qcloud_iot_file_manage.h
new file mode 100644
index 00000000..d918b156
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/services/explorer/qcloud_iot_file_manage.h
@@ -0,0 +1,179 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_file_manage.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-01-11
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-01-11 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_EXPLORER_QCLOUD_IOT_FILE_MANAGE_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_EXPLORER_QCLOUD_IOT_FILE_MANAGE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#include
+
+#include "qcloud_iot_common.h"
+
+/**
+ * @brief Define of max size using in file manage.
+ *
+ */
+#define MAX_SIZE_OF_FILE_MANAGE_FILE_NAME 64
+#define MAX_SIZE_OF_FILE_MANAGE_FILE_TYPE 10
+#define MAX_SIZE_OF_FILE_MANAGE_FILE_VERSION 64
+
+/**
+ * @brief FileManage report type.
+ *
+ */
+typedef enum {
+ // file update
+ IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOADING = 0,
+ IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_BEGIN,
+ IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_SUCCESS,
+ IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOAD_TIMEOUT,
+ IOT_FILE_MANAGE_REPORT_TYPE_FILE_NOT_EXIST,
+ IOT_FILE_MANAGE_REPORT_TYPE_AUTH_FAIL,
+ IOT_FILE_MANAGE_REPORT_TYPE_MD5_NOT_MATCH,
+ IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_FAIL,
+ IOT_FILE_MANAGE_REPORT_TYPE_SPACE_NOT_ENOUGH,
+ // file delete
+ IOT_FILE_MANAGE_REPORT_TYPE_DEL_SUCCESS,
+ IOT_FILE_MANAGE_REPORT_TYPE_DEL_FAIL,
+ // file post
+ IOT_FILE_MANAGE_REPORT_TYPE_POST_SUCCESS,
+ IOT_FILE_MANAGE_REPORT_TYPE_POST_FAIL,
+} IotFileManageReportType;
+
+/**
+ * @brief FileManage file type. @see sg_file_manage_file_type_str
+ *
+ */
+typedef enum {
+ IOT_FILE_MANAGE_FILE_TYPE_UNKOWN = -1,
+ IOT_FILE_MANAGE_FILE_TYPE_FILE = 0,
+ IOT_FILE_MANAGE_FILE_TYPE_AUDIO,
+ IOT_FILE_MANAGE_FILE_TYPE_VOICE,
+ IOT_FILE_MANAGE_FILE_TYPE_VIDEO,
+} IotFileManageFileType;
+
+/**
+ * @brief File info.
+ *
+ */
+typedef struct {
+ char file_name[MAX_SIZE_OF_FILE_MANAGE_FILE_NAME];
+ char file_version[MAX_SIZE_OF_FILE_MANAGE_FILE_VERSION];
+ IotFileManageFileType file_type;
+} IotFileManageFileInfo;
+
+/**
+ * @brief Callback of FileManage.
+ *
+ */
+typedef struct {
+ void (*update_file_callback)(UtilsJsonValue file_name, UtilsJsonValue file_type, UtilsJsonValue version,
+ UtilsJsonValue url, UtilsJsonValue md5sum, uint32_t file_size, void *usr_data);
+ void (*del_file_callback)(UtilsJsonValue file_name, UtilsJsonValue file_type, UtilsJsonValue version,
+ void *usr_data);
+ void (*report_file_version_reponse_callback)(UtilsJsonValue file_list, int result_code, void *usr_data);
+ void (*request_file_url_response_callback)(UtilsJsonValue url, UtilsJsonValue file_token, int result_code,
+ void *usr_data);
+} IotFileManageCallback;
+
+/**
+ * @brief File manage init, register handler to server list.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] callback @see IotFileManageCallback
+ * @param[in] usr_data usr data used in callback
+ * @return 0 for success, or err code (<0) @see IotReturnCode
+ */
+int IOT_FileManage_Init(void *client, IotFileManageCallback callback, void *usr_data);
+
+/**
+ * @brief File manage deinit, unregister handler from server list.
+ *
+ * @param[in,out] client pointer to mqtt client
+ */
+void IOT_FileManage_Deinit(void *client);
+
+/**
+ * @brief Report file manage message.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[out] buf publish message buffer
+ * @param[in] buf_len buffer len
+ * @param[in] report_type @see IotFileManageReportType
+ * @param[in] progress progress using in IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOADING
+ * @param[in] file_name_or_token token using in post event;file name using in other event
+ * @param[in] version file version
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_FileManage_Report(void *client, char *buf, int buf_len, IotFileManageReportType report_type, int progress,
+ const char *file_name_or_token, const char *version);
+
+/**
+ * @brief Report file list to server mqtt topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[out] buf publish message buffer
+ * @param[in] buf_len buffer len
+ * @param[in] file_list file list of @see IotFileManageFileInfo; file name which is "\0" means invalid.
+ * @param[in] max_num max num of file list.
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_FileManage_ReportFileList(void *client, char *buf, int buf_len, const IotFileManageFileInfo file_list[],
+ int max_num);
+/**
+ * @brief Request url to upload.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[out] buf publish message buffer
+ * @param[in] buf_len buffer len
+ * @param[in] file_info file to upload, @see IotFileManageFileInfo
+ * @param[in] request_id user defined, to keep unique.
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_FileManage_PostRequest(void *client, char *buf, int buf_len, const IotFileManageFileInfo *file_info,
+ int request_id);
+
+/**
+ * @brief Get file type according to string. @see sg_file_manage_file_type_str.
+ *
+ * @param[in] file_type file type string
+ * @param[in] len string length
+ * @return @see IotFileManageFileType
+ */
+IotFileManageFileType IOT_FileManage_GetFileType(const char *file_type, int len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_EXPLORER_QCLOUD_IOT_FILE_MANAGE_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/services/explorer/qcloud_iot_gateway_scene.h b/components/connectivity/iot-hub-device-c-sdk/include/services/explorer/qcloud_iot_gateway_scene.h
new file mode 100644
index 00000000..969aec30
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/services/explorer/qcloud_iot_gateway_scene.h
@@ -0,0 +1,113 @@
+/**
+ * @file qcloud_iot_gateway_scene.h
+ * @author {hubert} ({hubertxxu@tencent.com})
+ * @brief
+ * @version 1.0
+ * @date 2022-06-15
+ *
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-06-15 1.0 hubertxxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_EXPLORER_QCLOUD_IOT_GATEWAY_SCENE_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_EXPLORER_QCLOUD_IOT_GATEWAY_SCENE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#include
+
+#include "qcloud_iot_common.h"
+
+#define MAX_LENGTH_INNER_SCENE_ID (64)
+#define MAX_LENGTH_INNER_SCENE_NAME (64)
+#define MAX_LENGTH_INNER_SCENE_LIST (20)
+
+/**
+ * @brief callback of gateway scene
+ *
+ */
+typedef struct {
+ int (*gateway_scene_handles_callback)(UtilsJsonValue scene_id, UtilsJsonValue scene_name,
+ UtilsJsonValue scene_update_time, UtilsJsonValue scene_handles,
+ void *usr_data);
+ int (*gateway_run_scene_callback)(UtilsJsonValue scene_id, void *usr_data);
+ int (*gateway_delete_scene_callback)(UtilsJsonValue scene_id, void *usr_data);
+ int (*gateway_reload_scene_reply_callback)(int result_code, UtilsJsonValue status, UtilsJsonValue scene_result,
+ void *usr_data);
+} IoTGatewaySceneCallback;
+
+/**
+ * @brief gateway scene inner list
+ *
+ */
+typedef struct {
+ char inner_scene_id[MAX_LENGTH_INNER_SCENE_ID];
+ char inner_scene_name[MAX_LENGTH_INNER_SCENE_NAME];
+} IoTGatewaySceneInnerList;
+
+/**
+ * @brief gateway scene init, register handler to server list.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] callback @see IoTGatewaySceneCallback
+ * @param[in] usr_data usr data used in callback
+ * @return 0 for success, or err code (<0) @see IotReturnCode
+ */
+int IOT_GatewayScene_Init(void *client, IoTGatewaySceneCallback callback, void *usr_data);
+
+/**
+ * @brief Gateway scene deinit, unregister handler from server list.
+ *
+ * @param[in,out] client pointer to mqtt client
+ */
+void IOT_GatewayScene_Deinit(void *client);
+
+/**
+ * @brief reload gateway scene from cloud.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[out] buf publish message buffer
+ * @param[in] buf_len buffer len
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_GatewayScene_Reload(void *client, char *buf, int buf_len);
+
+/**
+ * @brief report gateway local scene
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[out] buf publish message buffer
+ * @param[in] buf_len buffer len
+ * @param list local scene list
+ * @param list_count local scene list count
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_GatewayScene_ReportInnerList(void *client, char *buf, int buf_len, IoTGatewaySceneInnerList *list,
+ int list_count);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_EXPLORER_QCLOUD_IOT_GATEWAY_SCENE_H_
\ No newline at end of file
diff --git a/components/connectivity/iot-hub-device-c-sdk/include/services/hub/qcloud_iot_broadcast.h b/components/connectivity/iot-hub-device-c-sdk/include/services/hub/qcloud_iot_broadcast.h
new file mode 100644
index 00000000..f0140ce7
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/include/services/hub/qcloud_iot_broadcast.h
@@ -0,0 +1,70 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_broadcast.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-07-18
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-07-18 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_HUB_QCLOUD_IOT_BROADCAST_H_
+#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_HUB_QCLOUD_IOT_BROADCAST_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#include
+
+#include "qcloud_iot_common.h"
+
+/**
+ * @brief Callback when broadcast message arrived.
+ *
+ */
+typedef void (*OnBroadcastArrivedCallback)(void *client, const char *msg, int msg_len, void *usr_data);
+
+/**
+ * @brief Subscribe broadcast topic with callback.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] callback callback to handle message
+ * @param[in] usr_data usr data using in callback
+ * @return @see IotReturnCode
+ */
+int IOT_Broadcast_Init(void *client, OnBroadcastArrivedCallback callback, void *usr_data);
+
+/**
+ * @brief Unsubscribe broadcast topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return @see IotReturnCode
+ */
+int IOT_Broadcast_Deinit(void *client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_HUB_QCLOUD_IOT_BROADCAST_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/platform/CMakeLists.txt b/components/connectivity/iot-hub-device-c-sdk/platform/CMakeLists.txt
new file mode 100644
index 00000000..bfae9774
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/platform/CMakeLists.txt
@@ -0,0 +1,9 @@
+file(GLOB src
+ ${CMAKE_CURRENT_SOURCE_DIR}/network/src/*.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/timer/src/*.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/os/${PLATFORM}/*.c
+)
+set(inc ${CMAKE_CURRENT_SOURCE_DIR}/network/inc/)
+
+set(src_platform ${src_platform} ${src} PARENT_SCOPE)
+set(inc_platform ${inc_platform} ${inc} PARENT_SCOPE)
diff --git a/components/connectivity/iot-hub-device-c-sdk/platform/at_client/CMakeLists.txt b/components/connectivity/iot-hub-device-c-sdk/platform/at_client/CMakeLists.txt
new file mode 100644
index 00000000..ef0e19b8
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/platform/at_client/CMakeLists.txt
@@ -0,0 +1,5 @@
+file(GLOB src ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
+set(inc ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
+
+set(src_platform ${src_platform} ${src} PARENT_SCOPE)
+set(inc_platform ${inc_platform} ${inc} PARENT_SCOPE)
diff --git a/components/connectivity/iot-hub-device-c-sdk/platform/at_client/inc/qcloud_iot_at_client.h b/components/connectivity/iot-hub-device-c-sdk/platform/at_client/inc/qcloud_iot_at_client.h
new file mode 100644
index 00000000..d82268db
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/platform/at_client/inc/qcloud_iot_at_client.h
@@ -0,0 +1,205 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_at_client.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-05-06
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-05-06 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_PLATFORM_AT_CLIENT_INC_QCLOUD_IOT_AT_CLIENT_H_
+#define IOT_HUB_DEVICE_C_SDK_PLATFORM_AT_CLIENT_INC_QCLOUD_IOT_AT_CLIENT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "qcloud_iot_platform.h"
+
+#define QCLOUD_AT_PARSE_THREAD_NAME "at_parse"
+#define QCLOUD_AT_PARSE_THREAD_STACK_SIZE (4096)
+#define QCLOUD_AT_PARSE_THREAD_PRIORITY THREAD_PRIORITY_HIGH
+
+#define QCLOUD_AT_URC_THREAD_NAME "at_urc"
+#define QCLOUD_AT_URC_THREAD_STACK_SIZE QCLOUD_AT_PARSE_THREAD_STACK_SIZE
+#define QCLOUD_AT_URC_THREAD_PRIORITY THREAD_PRIORITY_HIGH
+#define QCLOUD_AT_MAX_URC_QUEUE_LEN (16)
+
+typedef enum {
+ QCLOUD_AT_RET_SUCCESS = 0,
+ QCLOUD_AT_RET_INITTED = 1,
+ QCLOUD_AT_ERR_FAILURE = -1,
+ QCLOUD_AT_ERR_NO_INIT = -2,
+ QCLOUD_AT_ERR_TIMEOUT = -3,
+ QCLOUD_AT_ERR_SEND_FAIL = -4,
+ QCLOUD_AT_ERR_RESP_FAIL = -5,
+ QCLOUD_AT_ERR_BUFF_SHORT = -5,
+} QCloudATErrCode;
+
+/**
+ * @brief AT urc.
+ *
+ */
+typedef struct {
+ const char *urc_prefix;
+ OnUrcHandler urc_handle;
+} QcloudATUrc;
+
+/**
+ * @brief Send data to at module.
+ *
+ */
+typedef int (*QcloudATSendDataFunc)(const void *data, size_t data_len);
+
+/**
+ * @brief AT init status.
+ *
+ */
+typedef enum {
+ QCLOUD_AT_STATUS_NO_INIT = 0,
+ QCLOUD_AT_STATUS_INIT = 1,
+} QcloudATStatus;
+
+/**
+ * @brief AT response status.
+ *
+ */
+typedef enum {
+ QCLOUD_AT_RESP_STATUS_IDLE = 0, /* no change resp status */
+ QCLOUD_AT_RESP_STATUS_WAIT = 1, /* AT response wait */
+ QCLOUD_AT_RESP_STATUS_OK = 2, /* AT response OK */
+ QCLOUD_AT_RESP_STATUS_ERROR = -1, /* AT response ERROR */
+} QcloudATRespStatus;
+
+/**
+ * @brief AT response.
+ *
+ */
+typedef struct {
+ const char *expect;
+ uint8_t *recv_buf; /* recv data from at buffer */
+ uint32_t recv_len; /* recv data length */
+ QcloudATRespStatus status;
+} QcloudATResp;
+
+/**
+ * @brief AT URC Recv handle.
+ *
+ */
+typedef struct {
+ QcloudATUrc *urc;
+ char *urc_handle_buf;
+ uint32_t urc_recv_len;
+} QcloudATUrcRecv;
+
+/**
+ * @brief AT client.
+ *
+ */
+typedef struct {
+ QcloudATStatus status;
+ QcloudATSendDataFunc at_send_func;
+ // recv data
+ void *recv_pool;
+ void *recv_queue;
+ char *recv_buf;
+ uint32_t recv_buf_size;
+ uint32_t recv_len;
+ // urc
+ QcloudATUrc *urc_table;
+ uint32_t urc_table_size;
+ void *urc_recv_pool;
+ void *urc_recv_queue;
+ void *urc_lock;
+ // resp
+ QcloudATResp resp;
+ void *resp_lock;
+ void *resp_sem;
+} QcloudATClient;
+
+/**
+ * @brief Parse data recv from at module.
+ *
+ * @param[in] client pointer to QcloudATClient
+ */
+void qcloud_iot_at_client_parser(void *client);
+
+/**
+ * @brief Recv urc and callback.
+ *
+ * @param[in] client pointer to QcloudATClient
+ */
+void qcloud_iot_at_urc_handle(void *client);
+
+/**
+ * @brief Init at client.
+ *
+ * @param[in] max_at_size max at buffer size
+ * @param[in] at_send_func send at function
+ * @return @see QCloudATErrCode
+ */
+int qcloud_iot_at_client_init(int max_at_size, QcloudATSendDataFunc at_send_func);
+
+/**
+ * @brief Set at urc.
+ *
+ * @param[in] urc_table urc table
+ * @param[in] urc_table_size table size
+ */
+void qcloud_iot_at_client_set_urc(QcloudATUrc *urc_table, uint32_t urc_table_size);
+
+/**
+ * @brief Send at command and wait for receiving expect response or data.
+ *
+ * @param[in] at_cmd at command
+ * @param[in] at_expect expect response or data length format
+ * @param[out] recv_buf recv data buffer
+ * @param[out] recv_len recv data length
+ * @param[in] timeout_ms wait timeout
+ * @return @see QCloudATErrCode
+ */
+int qcloud_iot_at_client_send_at_util_expect(const char *at_cmd, const char *at_expect, void *recv_buf,
+ uint32_t *recv_len, uint32_t timeout_ms);
+
+/**
+ * @brief Send data to at module.
+ *
+ * @param[in] data
+ * @param[in] data_len
+ * @return 0 for success
+ */
+int qcloud_iot_at_client_send_data(const void *data, size_t data_len);
+
+/**
+ * @brief Push data to at client queue.
+ *
+ * @param[in] recv_data recv data from uart
+ * @param[in] data_len recv data length
+ * @return 0 for success.
+ */
+int qcloud_iot_at_client_push_data(const void *recv_data, size_t data_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_PLATFORM_AT_CLIENT_INC_QCLOUD_IOT_AT_CLIENT_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/platform/at_client/src/qcloud_iot_at_client.c b/components/connectivity/iot-hub-device-c-sdk/platform/at_client/src/qcloud_iot_at_client.c
new file mode 100644
index 00000000..2fbc2b61
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/platform/at_client/src/qcloud_iot_at_client.c
@@ -0,0 +1,256 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_at_client.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-05-06
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-05-06 1.0 fancyxu first commit
+ *
+ */
+
+#include "qcloud_iot_at_client.h"
+
+/**
+ * @brief Only one client is support.
+ *
+ */
+static QcloudATClient sg_client;
+
+/**
+ * @brief Init at client.
+ *
+ * @param[in] max_at_size max at buffer size
+ * @param[in] at_send_func send at function
+ * @return @see QCloudATErrCode
+ */
+int qcloud_iot_at_client_init(int max_at_size, QcloudATSendDataFunc at_send_func)
+{
+ if (QCLOUD_AT_STATUS_INIT == sg_client.status) {
+ return QCLOUD_AT_RET_INITTED;
+ }
+ sg_client.status = QCLOUD_AT_STATUS_INIT;
+
+ int rc;
+
+ QcloudATClient *client = &sg_client;
+ client->at_send_func = at_send_func;
+
+ client->recv_pool = HAL_Malloc(max_at_size);
+ if (!client->recv_pool) {
+ goto exit;
+ }
+
+ client->recv_queue = HAL_MailQueueInit(client->recv_pool, 1, max_at_size);
+ if (!client->recv_queue) {
+ goto exit;
+ }
+ client->recv_buf = HAL_Malloc(max_at_size);
+ if (!client->recv_buf) {
+ goto exit;
+ }
+ client->recv_buf_size = max_at_size;
+ client->recv_len = 0;
+
+ client->urc_table = NULL;
+ client->urc_table_size = 0;
+
+ client->urc_recv_pool = HAL_Malloc(sizeof(QcloudATUrcRecv) * QCLOUD_AT_MAX_URC_QUEUE_LEN);
+ if (!client->urc_recv_pool) {
+ goto exit;
+ }
+
+ client->urc_recv_queue =
+ HAL_MailQueueInit(client->urc_recv_pool, sizeof(QcloudATUrcRecv), QCLOUD_AT_MAX_URC_QUEUE_LEN);
+ if (!client->urc_recv_queue) {
+ goto exit;
+ }
+
+ client->resp.expect = NULL;
+ client->resp.status = QCLOUD_AT_RESP_STATUS_IDLE;
+ client->resp_lock = HAL_MutexCreate();
+ if (!client->resp_lock) {
+ goto exit;
+ }
+ client->resp_sem = HAL_SemaphoreCreate();
+ if (!client->resp_sem) {
+ goto exit;
+ }
+
+ static ThreadParams sg_qcloud_at_parse_thread_params = {0};
+ static void *sg_qcloud_at_parse_thread_stack;
+ if (!sg_qcloud_at_parse_thread_stack) {
+ sg_qcloud_at_parse_thread_stack = HAL_Malloc(QCLOUD_AT_PARSE_THREAD_STACK_SIZE);
+ if (!sg_qcloud_at_parse_thread_stack) {
+ goto exit;
+ }
+ }
+ sg_qcloud_at_parse_thread_params.user_arg = client;
+ sg_qcloud_at_parse_thread_params.stack_base = sg_qcloud_at_parse_thread_stack;
+ sg_qcloud_at_parse_thread_params.stack_size = QCLOUD_AT_PARSE_THREAD_STACK_SIZE;
+ sg_qcloud_at_parse_thread_params.thread_name = QCLOUD_AT_PARSE_THREAD_NAME;
+ sg_qcloud_at_parse_thread_params.priority = QCLOUD_AT_PARSE_THREAD_PRIORITY;
+ sg_qcloud_at_parse_thread_params.thread_func = qcloud_iot_at_client_parser;
+
+ rc = HAL_ThreadCreate(&sg_qcloud_at_parse_thread_params);
+ if (rc) {
+ goto exit;
+ }
+
+ static ThreadParams sg_qcloud_at_urc_thread_params = {0};
+ static void *sg_qcloud_at_urc_thread_stack;
+ if (!sg_qcloud_at_urc_thread_stack) {
+ sg_qcloud_at_urc_thread_stack = HAL_Malloc(QCLOUD_AT_URC_THREAD_STACK_SIZE);
+ if (!sg_qcloud_at_urc_thread_stack) {
+ goto exit;
+ }
+ }
+ sg_qcloud_at_urc_thread_params.user_arg = client;
+ sg_qcloud_at_urc_thread_params.stack_base = sg_qcloud_at_urc_thread_stack;
+ sg_qcloud_at_urc_thread_params.stack_size = QCLOUD_AT_URC_THREAD_STACK_SIZE;
+ sg_qcloud_at_urc_thread_params.thread_name = QCLOUD_AT_URC_THREAD_NAME;
+ sg_qcloud_at_urc_thread_params.priority = QCLOUD_AT_URC_THREAD_PRIORITY;
+ sg_qcloud_at_urc_thread_params.thread_func = qcloud_iot_at_urc_handle;
+
+ rc = HAL_ThreadCreate(&sg_qcloud_at_urc_thread_params);
+ if (rc) {
+ HAL_ThreadDestroy(sg_qcloud_at_parse_thread_params.thread_id);
+ goto exit;
+ }
+ return QCLOUD_AT_RET_SUCCESS;
+exit:
+ HAL_MailQueueDeinit(client->recv_queue);
+ HAL_Free(client->recv_pool);
+ HAL_Free(client->recv_buf);
+ HAL_MutexDestroy(client->resp_lock);
+ HAL_SemaphoreDestroy(client->resp_sem);
+ HAL_MailQueueDeinit(client->urc_recv_queue);
+ HAL_Free(client->urc_recv_pool);
+ memset(client, 0, sizeof(QcloudATClient));
+ return QCLOUD_AT_ERR_FAILURE;
+}
+
+/**
+ * @brief Set at urc.
+ *
+ * @param[in] urc_table urc table
+ * @param[in] urc_table_size table size
+ */
+void qcloud_iot_at_client_set_urc(QcloudATUrc *urc_table, uint32_t urc_table_size)
+{
+ sg_client.urc_table = urc_table;
+ sg_client.urc_table_size = urc_table_size;
+}
+
+/**
+ * @brief Send at command and wait for receiving expect response.
+ *
+ * @param[in] at_cmd at command
+ * @param[in] at_expect expect response
+ * @param[in] timeout_ms wait timeout
+ * @return @see QCloudATErrCode
+ */
+
+/**
+ * @brief Send at command and wait for receiving expect response or data.
+ *
+ * @param[in] at_cmd at command
+ * @param[in] at_expect expect response or data length format
+ * @param[out] recv_buf recv data buffer
+ * @param[out] recv_len recv data length
+ * @param[in] timeout_ms wait timeout
+ * @return @see QCloudATErrCode
+ */
+int qcloud_iot_at_client_send_at_util_expect(const char *at_cmd, const char *at_expect, void *recv_buf,
+ uint32_t *recv_len, uint32_t timeout_ms)
+{
+ QcloudATClient *client = &sg_client;
+ if (QCLOUD_AT_STATUS_NO_INIT == client->status) {
+ return QCLOUD_AT_ERR_NO_INIT;
+ }
+ int rc;
+
+ HAL_MutexLock(client->resp_lock);
+ client->resp.expect = at_expect;
+ client->resp.recv_buf = recv_buf;
+ client->resp.recv_len = 0;
+ client->resp.status = QCLOUD_AT_RESP_STATUS_WAIT;
+
+ rc = client->at_send_func(at_cmd, strlen(at_cmd));
+ if (rc) {
+ rc = QCLOUD_AT_ERR_SEND_FAIL;
+ goto exit;
+ }
+
+ rc = HAL_SemaphoreWait(client->resp_sem, timeout_ms);
+ if (rc) {
+ rc = QCLOUD_AT_ERR_TIMEOUT;
+ goto exit;
+ }
+
+ if (client->resp.status != QCLOUD_AT_RESP_STATUS_OK) {
+ rc = QCLOUD_AT_ERR_RESP_FAIL;
+ }
+ *recv_len = client->resp.recv_len;
+exit:
+ client->resp.expect = NULL;
+ client->resp.status = QCLOUD_AT_RESP_STATUS_IDLE;
+ HAL_MutexUnlock(client->resp_lock);
+ return rc;
+}
+
+/**
+ * @brief Send data to at module.
+ *
+ * @param[in] data
+ * @param[in] data_len
+ * @return 0 for success
+ */
+int qcloud_iot_at_client_send_data(const void *data, size_t data_len)
+{
+ QcloudATClient *client = &sg_client;
+ if (QCLOUD_AT_STATUS_NO_INIT == client->status) {
+ return QCLOUD_AT_ERR_NO_INIT;
+ }
+ return client->at_send_func(data, data_len);
+}
+
+/**
+ * @brief Push data to at client queue.
+ *
+ * @param[in] recv_data recv data from uart
+ * @param[in] data_len recv data length
+ * @return 0 for success.
+ */
+int qcloud_iot_at_client_push_data(const void *recv_data, size_t data_len)
+{
+ int rc = 0;
+ QcloudATClient *client = &sg_client;
+ if (QCLOUD_AT_STATUS_NO_INIT == client->status) {
+ return QCLOUD_AT_ERR_NO_INIT;
+ }
+ for (size_t i = 0; i < data_len; i++) {
+ rc = HAL_MailQueueSend(client->recv_queue, (uint8_t *)recv_data + i, 1);
+ if (rc) {
+ return QCLOUD_AT_ERR_FAILURE;
+ }
+ }
+ return QCLOUD_AT_RET_SUCCESS;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/platform/at_client/src/qcloud_iot_at_client_parse.c b/components/connectivity/iot-hub-device-c-sdk/platform/at_client/src/qcloud_iot_at_client_parse.c
new file mode 100644
index 00000000..708f30b9
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/platform/at_client/src/qcloud_iot_at_client_parse.c
@@ -0,0 +1,259 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_at_client_parse.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-04-27
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-04-27 1.0 fancyxu first commit
+ *
+ */
+
+#include "qcloud_iot_at_client.h"
+
+#include
+
+/**
+ * @brief Max recv timeout.
+ *
+ */
+#define MAX_RECV_TIMEOUT 0xffffffffUL
+
+/**
+ * @brief Get expect len from expect.
+ *
+ * @param[in] client pointer to QcloudATClient
+ */
+static void _at_client_get_expect_len(QcloudATClient *client, uint32_t *expect_len)
+{
+ int len = 0;
+ if (sscanf(client->recv_buf, client->resp.expect, &len) == 1 &&
+ client->recv_buf[client->recv_len - 1] == client->resp.expect[strlen(client->resp.expect) - 1]) {
+ *expect_len = len + client->recv_len;
+ client->resp.recv_len = len;
+ }
+}
+
+/**
+ * @brief Read one line for at recv queue.
+ *
+ * @param[in] client pointer to QcloudATClient
+ * @return @see QCloudATErrCode
+ */
+static int _at_client_readline(QcloudATClient *client)
+{
+ int rc, read_len = 0;
+ char ch = 0, last_ch = 0;
+ size_t read_size = 1;
+ uint32_t expect_len = 0;
+
+ memset(client->recv_buf, 0, client->recv_buf_size);
+ client->recv_len = 0;
+
+ while (1) {
+ rc = HAL_MailQueueRecv(client->recv_queue, &ch, &read_size, MAX_RECV_TIMEOUT);
+ if (rc) {
+ Log_e("recv from queue fail, %d", rc);
+ return QCLOUD_AT_ERR_BUFF_SHORT;
+ }
+
+ if (read_len >= client->recv_buf_size) {
+ Log_e("read line failed. The line data length is out of buffer size(%d)!", client->recv_buf_size);
+ client->recv_len = 0;
+ return QCLOUD_AT_ERR_BUFF_SHORT;
+ }
+
+ client->recv_buf[read_len++] = ch;
+ client->recv_len = read_len;
+
+ // check if using expect string to get length
+ if (client->resp.recv_buf) {
+ if (!client->resp.recv_len) {
+ _at_client_get_expect_len(client, &expect_len);
+ }
+
+ if (expect_len) {
+ if (expect_len == client->recv_len) {
+ break;
+ }
+ continue;
+ }
+ }
+
+ if ((ch == '\n' && last_ch == '\r')) {
+ break;
+ }
+ last_ch = ch;
+ }
+ client->recv_buf[read_len] = '\0';
+ return QCLOUD_AT_RET_SUCCESS;
+}
+
+/**
+ * @brief Check urc table.
+ *
+ * @param[in] client pointer to QcloudATClient
+ * @return @see QCloudATErrCode
+ */
+static int _at_client_check_urc(QcloudATClient *client)
+{
+ int i;
+ size_t urc_len;
+ QcloudATUrc *urc;
+
+ for (i = 0; i < client->urc_table_size; i++) {
+ urc = &client->urc_table[i];
+ urc_len = strlen(urc->urc_prefix);
+
+ if (client->recv_len < urc_len) {
+ continue;
+ }
+
+ if (!strncmp(urc->urc_prefix, client->recv_buf, urc_len)) {
+ QcloudATUrcRecv urc_recv;
+ urc_recv.urc = urc;
+ urc_recv.urc_recv_len = client->recv_len;
+ urc_recv.urc_handle_buf = HAL_Malloc(client->recv_len + 1);
+ if (!urc_recv.urc_handle_buf) {
+ Log_e("malloc urc_handle_buf error.");
+ return QCLOUD_AT_ERR_FAILURE;
+ }
+ memcpy(urc_recv.urc_handle_buf, client->recv_buf, client->recv_len);
+ urc_recv.urc_handle_buf[client->recv_len] = '\0';
+ return HAL_MailQueueSend(client->urc_recv_queue, &urc_recv, sizeof(QcloudATUrcRecv));
+ }
+ }
+ return QCLOUD_AT_ERR_FAILURE;
+}
+
+/**
+ * @brief Check at response.
+ *
+ * @param[in] client pointer to QcloudATClient
+ */
+static int _at_client_check_common_resp(QcloudATClient *client)
+{
+ if (!strncmp(client->recv_buf, "OK", 2) && !client->resp.expect) {
+ client->resp.status = QCLOUD_AT_RESP_STATUS_OK;
+ return QCLOUD_AT_RET_SUCCESS;
+ }
+
+ if (strstr(client->recv_buf, "ERROR") || !strncmp(client->recv_buf, "FAIL", 4)) {
+ client->resp.status = QCLOUD_AT_RESP_STATUS_ERROR;
+ return QCLOUD_AT_ERR_RESP_FAIL;
+ }
+ return QCLOUD_AT_ERR_FAILURE;
+}
+
+/**
+ * @brief Check expect response.
+ *
+ * @param[in] client pointer to QcloudATClient
+ * @return @see QCloudATErrCode
+ */
+static int _at_client_check_expect(QcloudATClient *client)
+{
+ if (!client->resp.expect) {
+ return QCLOUD_AT_ERR_FAILURE;
+ }
+
+ if (client->resp.recv_buf && client->resp.recv_len) {
+ memcpy(client->resp.recv_buf, client->recv_buf + client->recv_len - client->resp.recv_len,
+ client->resp.recv_len);
+ client->resp.status = QCLOUD_AT_RESP_STATUS_OK;
+ return QCLOUD_AT_RET_SUCCESS;
+ }
+
+ if (strncmp(client->resp.expect, client->recv_buf, strlen(client->resp.expect))) {
+ return QCLOUD_AT_ERR_FAILURE;
+ }
+
+ client->resp.status = QCLOUD_AT_RESP_STATUS_OK;
+ return QCLOUD_AT_RET_SUCCESS;
+}
+
+/**
+ * @brief Parse data recv from at module.
+ *
+ * @param[in] client pointer to QcloudATClient
+ */
+void qcloud_iot_at_client_parser(void *client)
+{
+ int rc;
+
+ QcloudATClient *at_client = (QcloudATClient *)client;
+ while (1) {
+ rc = _at_client_readline(at_client);
+ if (rc) {
+ continue;
+ }
+ printf("AT recv <--:%.*s\r\n", at_client->recv_len, at_client->recv_buf);
+
+ rc = _at_client_check_urc(at_client);
+ if (!rc) {
+ continue;
+ }
+
+ if (QCLOUD_AT_RESP_STATUS_WAIT != at_client->resp.status) {
+ continue;
+ }
+
+ rc = _at_client_check_expect(at_client);
+ if (!rc) {
+ HAL_SemaphorePost(at_client->resp_sem);
+ continue;
+ }
+
+ rc = _at_client_check_common_resp(at_client);
+ switch (rc) {
+ case QCLOUD_AT_RET_SUCCESS:
+ // if expect, ignore "OK"
+ if (at_client->resp.expect) {
+ break;
+ }
+ case QCLOUD_AT_ERR_RESP_FAIL:
+ HAL_SemaphorePost(at_client->resp_sem);
+ break;
+ default:
+ // Log_w("ignore %.*s", at_client->recv_len, at_client->recv_buf);
+ break;
+ }
+ }
+}
+
+/**
+ * @brief Recv urc and callback.
+ *
+ * @param[in] client pointer to QcloudATClient
+ */
+void qcloud_iot_at_urc_handle(void *client)
+{
+ QcloudATClient *at_client = (QcloudATClient *)client;
+ QcloudATUrcRecv urc_recv;
+ size_t recv_size = sizeof(QcloudATUrcRecv);
+ while (1) {
+ int rc = HAL_MailQueueRecv(at_client->urc_recv_queue, &urc_recv, &recv_size, MAX_RECV_TIMEOUT);
+ if (rc) {
+ continue;
+ }
+ urc_recv.urc->urc_handle(urc_recv.urc_handle_buf, urc_recv.urc_recv_len);
+ HAL_Free(urc_recv.urc_handle_buf);
+ }
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/platform/network/inc/network_interface.h b/components/connectivity/iot-hub-device-c-sdk/platform/network/inc/network_interface.h
new file mode 100644
index 00000000..434a4fa1
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/platform/network/inc/network_interface.h
@@ -0,0 +1,108 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file network_interface.h
+ * @brief header for network interface
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-28
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-28 1.0 fancyxu first commit
+ * 2021-07-09 1.1 fancyxu support tls and change port to str format
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_PLATFORM_NETWORK_INC_NETWORK_INTERFACE_H_
+#define IOT_HUB_DEVICE_C_SDK_PLATFORM_NETWORK_INC_NETWORK_INTERFACE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+
+#include "qcloud_iot_platform.h"
+
+#ifndef AUTH_WITH_NO_TLS
+#include "qcloud_iot_tls_client.h"
+#endif
+
+/**
+ * @brief Type of network interface.
+ *
+ */
+typedef enum {
+ IOT_NETWORK_TYPE_TCP = 0,
+ IOT_NETWORK_TYPE_UDP,
+ IOT_NETWORK_TYPE_TLS,
+ IOT_NETWORK_TYPE_DTLS
+} IotNetworkType;
+
+/**
+ * @brief Define structure for network stack.
+ *
+ */
+typedef struct IotNetwork IotNetwork;
+
+/**
+ * @brief Define structure for network stack.
+ *
+ * @note init/connect/read/write/disconnect/state
+ *
+ */
+struct IotNetwork {
+ int (*init)(IotNetwork *);
+
+ int (*connect)(IotNetwork *);
+
+ int (*read)(IotNetwork *, unsigned char *, size_t, uint32_t, size_t *);
+
+ int (*write)(IotNetwork *, unsigned char *, size_t, uint32_t, size_t *);
+
+ void (*disconnect)(IotNetwork *);
+
+ int (*is_connected)(IotNetwork *);
+
+ union {
+ int fd;
+ uintptr_t handle;
+ };
+
+#ifndef AUTH_WITH_NO_TLS
+ SSLConnectParams ssl_connect_params;
+#endif
+
+ const char * host; /**< server address */
+ const char * port; /**< server port */
+ IotNetworkType type;
+};
+
+/**
+ * @brief Init network, support tcp, tls(if AUTH_WITH_NO_TLS defined).
+ *
+ * @param[in,out] network pointer to network
+ * @return @see IotReturnCode
+ */
+int qcloud_iot_network_init(IotNetwork *network);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_PLATFORM_NETWORK_INC_NETWORK_INTERFACE_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/platform/network/src/network_interface.c b/components/connectivity/iot-hub-device-c-sdk/platform/network/src/network_interface.c
new file mode 100644
index 00000000..b0d17611
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/platform/network/src/network_interface.c
@@ -0,0 +1,259 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file network_interface.c
+ * @brief network interface for tcp/tls
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-31
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-31 1.0 fancyxu first commit
+ * 2021-07-09 1.1 fancyxu support tls
+ *
+ */
+
+#include "network_interface.h"
+
+/**
+ * @brief TCP init, do nothing.
+ *
+ * @param[in,out] network pointer to network handle
+ * @return always ok
+ */
+static int _network_tcp_init(IotNetwork *network)
+{
+ return QCLOUD_RET_SUCCESS;
+}
+
+/**
+ * @brief TCP connect.
+ *
+ * @param[in,out] network pointer to network handle
+ * @return @see IotReturnCode
+ */
+static int _network_tcp_connect(IotNetwork *network)
+{
+ POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
+
+ network->fd = HAL_TCP_Connect(network->host, network->port);
+
+ if (network->fd < 0) {
+ Log_e("fail to connect with TCP server: %s:%s", STRING_PTR_PRINT_SANITY_CHECK(network->host),
+ STRING_PTR_PRINT_SANITY_CHECK(network->port));
+ return network->fd;
+ }
+
+ if (!strstr(network->host, "gateway.tencentdevices.com") &&
+ !strstr(network->host, "devicelog.iot.cloud.tencent.com")) { // reduce log info
+ Log_i("connected with TCP server: %s:%s", STRING_PTR_PRINT_SANITY_CHECK(network->host),
+ STRING_PTR_PRINT_SANITY_CHECK(network->port));
+ }
+
+ return QCLOUD_RET_SUCCESS;
+}
+
+/**
+ * @brief TCP read.
+ *
+ * @param[in,out] network pointer to network handle
+ * @param[out] data data buffer to be read in
+ * @param[in] datalen data buffer len
+ * @param[in] timeout_ms read timeout
+ * @param[out] read_len read data len
+ * @return @see IotReturnCode
+ */
+static int _network_tcp_read(IotNetwork *network, unsigned char *data, size_t datalen, uint32_t timeout_ms,
+ size_t *read_len)
+{
+ POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
+
+ return HAL_TCP_Read(network->fd, data, (uint32_t)datalen, timeout_ms, read_len);
+}
+
+/**
+ * @brief TCP write.
+ *
+ * @param[in,out] network pointer to network handle
+ * @param[out] data data buffer to write
+ * @param[in] datalen data buffer len
+ * @param[in] timeout_ms write timeout
+ * @param[out] written_len len of written data
+ * @return @see IotReturnCode
+ */
+static int _network_tcp_write(IotNetwork *network, unsigned char *data, size_t datalen, uint32_t timeout_ms,
+ size_t *written_len)
+{
+ POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
+
+ return HAL_TCP_Write(network->fd, data, datalen, timeout_ms, written_len);
+}
+
+/**
+ * @brief TCP disconnect
+ *
+ * @param[in,out] network pointer to network handle
+ */
+static void _network_tcp_disconnect(IotNetwork *network)
+{
+ POINTER_SANITY_CHECK_RTN(network);
+
+ if (network->fd < 0) {
+ return;
+ }
+
+ HAL_TCP_Disconnect(network->fd);
+ network->fd = -1;
+ return;
+}
+
+/**
+ * @brief Return handle.
+ *
+ * @param[in] network pointer to network
+ * @return handle of network
+ */
+static int _is_network_tcp_connected(IotNetwork *network)
+{
+ return network->fd > 0;
+}
+
+#ifndef AUTH_WITH_NO_TLS
+
+/**
+ * @brief TLS init, do nothing.
+ *
+ * @param[in,out] network pointer to network handle
+ * @return always ok
+ */
+static int _network_tls_init(IotNetwork *network)
+{
+ return QCLOUD_RET_SUCCESS;
+}
+
+/**
+ * @brief TLS connect.
+ *
+ * @param[in,out] network pointer to network handle
+ * @return @see IotReturnCode
+ */
+static int _network_tls_connect(IotNetwork *network)
+{
+ POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
+ network->handle = qcloud_iot_tls_client_connect(&network->ssl_connect_params, network->host, network->port);
+ return network->handle ? QCLOUD_RET_SUCCESS : QCLOUD_ERR_SSL_CONNECT;
+}
+
+/**
+ * @brief TLS read.
+ *
+ * @param[in,out] network pointer to network handle
+ * @param[out] data data buffer to be read in
+ * @param[in] datalen data buffer len
+ * @param[in] timeout_ms read timeout
+ * @param[out] read_len read data len
+ * @return @see IotReturnCode
+ */
+static int _network_tls_read(IotNetwork *network, unsigned char *data, size_t datalen, uint32_t timeout_ms,
+ size_t *read_len)
+{
+ POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
+ return qcloud_iot_tls_client_read(network->handle, data, datalen, timeout_ms, read_len);
+}
+
+/**
+ * @brief TLS write.
+ *
+ * @param[in,out] network pointer to network handle
+ * @param[out] data data buffer to write
+ * @param[in] datalen data buffer len
+ * @param[in] timeout_ms write timeout
+ * @param[out] written_len len of written data
+ * @return @see IotReturnCode
+ */
+static int _network_tls_write(IotNetwork *network, unsigned char *data, size_t datalen, uint32_t timeout_ms,
+ size_t *written_len)
+{
+ POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
+ return qcloud_iot_tls_client_write(network->handle, data, datalen, timeout_ms, written_len);
+}
+
+/**
+ * @brief TLS disconnect
+ *
+ * @param[in,out] network pointer to network handle
+ */
+static void _network_tls_disconnect(IotNetwork *network)
+{
+ POINTER_SANITY_CHECK_RTN(network);
+
+ qcloud_iot_tls_client_disconnect(network->handle);
+ network->handle = 0;
+}
+
+/**
+ * @brief Return handle.
+ *
+ * @param[in] network pointer to network
+ * @return handle of network
+ */
+static int _is_network_tls_connected(IotNetwork *network)
+{
+ return network->handle;
+}
+
+#endif
+
+/**
+ * @brief Init network, support tcp, tls(if AUTH_WITH_NO_TLS defined).
+ *
+ * @param[in,out] network pointer to network
+ * @return @see IotReturnCode
+ */
+int qcloud_iot_network_init(IotNetwork *network)
+{
+ POINTER_SANITY_CHECK(network, QCLOUD_ERR_INVAL);
+
+ switch (network->type) {
+ case IOT_NETWORK_TYPE_TCP:
+ network->init = _network_tcp_init;
+ network->connect = _network_tcp_connect;
+ network->read = _network_tcp_read;
+ network->write = _network_tcp_write;
+ network->disconnect = _network_tcp_disconnect;
+ network->is_connected = _is_network_tcp_connected;
+ network->fd = -1;
+ break;
+
+#ifndef AUTH_WITH_NO_TLS
+ case IOT_NETWORK_TYPE_TLS:
+ network->init = _network_tls_init;
+ network->connect = _network_tls_connect;
+ network->read = _network_tls_read;
+ network->write = _network_tls_write;
+ network->disconnect = _network_tls_disconnect;
+ network->is_connected = _is_network_tls_connected;
+ network->handle = 0;
+ break;
+#endif
+
+ default:
+ Log_e("unknown network type: %d", network->type);
+ return QCLOUD_ERR_INVAL;
+ }
+ return network->init(network);
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/platform/os/tencentos_tiny/HAL_Device_tencentos_tiny.c b/components/connectivity/iot-hub-device-c-sdk/platform/os/tencentos_tiny/HAL_Device_tencentos_tiny.c
new file mode 100644
index 00000000..1df46ed0
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/platform/os/tencentos_tiny/HAL_Device_tencentos_tiny.c
@@ -0,0 +1,112 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file HAL_Device_tencentos_tiny.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-01-24
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-01-24 1.0 fancyxu first commit
+ *
+ */
+
+#include
+#include
+#include
+
+#include "qcloud_iot_platform.h"
+
+#include "qcloud_iot_params_check.h"
+
+/**
+ * @brief product Id
+ *
+ */
+static char sg_product_id[MAX_SIZE_OF_PRODUCT_ID + 1] = "Q5NNWVC2Z8";
+
+/**
+ * @brief device name
+ *
+ */
+static char sg_device_name[MAX_SIZE_OF_DEVICE_NAME + 1] = "test1";
+
+/**
+ * @brief device secret of PSK device
+ *
+ */
+static char sg_device_secret[MAX_SIZE_OF_DEVICE_SECRET + 1] = "jACrR7OalxdQfdJgEduZ3w==";
+
+/**
+ * @brief Copy device info from src to dst
+ *
+ * @param[out] dst dst to copy
+ * @param[in] src srt to be copied
+ * @param[in] max_len max_len to be copy
+ * @return @see IotReturnCode
+ */
+static int device_info_copy(void *pdst, void *psrc, uint8_t max_len)
+{
+ if (strlen(psrc) > max_len) {
+ return QCLOUD_ERR_FAILURE;
+ }
+ memset(pdst, '\0', max_len);
+ strncpy(pdst, psrc, max_len);
+ return QCLOUD_RET_SUCCESS;
+}
+
+/**
+ * @brief Save device info
+ *
+ * @param[in] device_info @see DeviceInfo
+ * @return @see IotReturnCode
+ */
+int HAL_SetDevInfo(DeviceInfo *device_info)
+{
+ POINTER_SANITY_CHECK(device_info, QCLOUD_ERR_DEV_INFO);
+ int rc;
+ rc = device_info_copy(sg_product_id, device_info->product_id, MAX_SIZE_OF_PRODUCT_ID); // set product ID
+ rc |= device_info_copy(sg_device_name, device_info->device_name, MAX_SIZE_OF_DEVICE_NAME); // set dev name
+ rc |= device_info_copy(sg_device_secret, device_info->device_secret, MAX_SIZE_OF_DEVICE_SECRET); // set dev secret
+ if (rc) {
+ Log_e("Set device info err");
+ rc = QCLOUD_ERR_DEV_INFO;
+ }
+ return rc;
+}
+
+/**
+ * @brief Get device info
+ *
+ * @param[in] device_info @see DeviceInfo
+ * @return @see IotReturnCode
+ */
+int HAL_GetDevInfo(DeviceInfo *device_info)
+{
+ POINTER_SANITY_CHECK(device_info, QCLOUD_ERR_DEV_INFO);
+ int ret;
+ memset(device_info, '\0', sizeof(DeviceInfo));
+ ret = device_info_copy(device_info->product_id, sg_product_id, MAX_SIZE_OF_PRODUCT_ID); // get product ID
+ ret |= device_info_copy(device_info->device_name, sg_device_name, MAX_SIZE_OF_DEVICE_NAME); // get dev name
+ ret |= device_info_copy(device_info->device_secret, sg_device_secret, MAX_SIZE_OF_DEVICE_SECRET); // get dev secret
+ if (QCLOUD_RET_SUCCESS != ret) {
+ Log_e("Get device info err");
+ ret = QCLOUD_ERR_DEV_INFO;
+ }
+ return ret;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/platform/os/tencentos_tiny/HAL_File_tencentos_tiny.c b/components/connectivity/iot-hub-device-c-sdk/platform/os/tencentos_tiny/HAL_File_tencentos_tiny.c
new file mode 100644
index 00000000..4e92d43a
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/platform/os/tencentos_tiny/HAL_File_tencentos_tiny.c
@@ -0,0 +1,76 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file HAL_File_tencentos_tiny.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-04-07
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-04-07 1.0 fancyxu first commit
+ *
+ */
+
+#include
+#include
+
+#include "qcloud_iot_platform.h"
+
+/**
+ * @brief Functions for saving file into NVS(files/FLASH)
+ * @param[in] filename file path name
+ * @param[in] buf source need write buffer
+ * @param[in] write_len length of file to write
+ * @return length of data save when success, or 0 for failure
+ */
+size_t HAL_File_Write(const char *filename, const void *buf, size_t write_len, size_t offset)
+{
+ return write_len;
+}
+
+/**
+ * @brief Functions for reading file from NVS(files/FLASH)
+ * @param[in] filename file path name
+ * @param[in] buf destination log buffer
+ * @param[in] read_len length to read
+ * @return length of data read when success, or 0 for failure
+ */
+size_t HAL_File_Read(const char *filename, void *buf, size_t read_len, size_t offset)
+{
+ return 0;
+}
+
+/**
+ * @brief Functions for deleting file in NVS(files/FLASH).
+ * @param[in] filename file path name
+ * @return 0 when success
+ */
+int HAL_File_Del(const char *filename)
+{
+ return 0;
+}
+
+/**
+ * @brief Functions for reading the size of file in NVS(files/FLASH).
+ * @param[in] filename file path name
+ * @return 0 when nothing exist
+ */
+size_t HAL_File_GetSize(const char *filename)
+{
+ return 0;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/platform/os/tencentos_tiny/HAL_Module_tencentos_tiny.c b/components/connectivity/iot-hub-device-c-sdk/platform/os/tencentos_tiny/HAL_Module_tencentos_tiny.c
new file mode 100644
index 00000000..c803fe38
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/platform/os/tencentos_tiny/HAL_Module_tencentos_tiny.c
@@ -0,0 +1,172 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file HAL_AT_Linux.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-04-21
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-04-21 1.0 fancyxu first commit
+ *
+ */
+
+#include "qcloud_iot_platform.h"
+
+#include "qcloud_iot_at_client.h"
+#include "tos_hal.h"
+
+#define YOUR_WIFI_SSID "youga_wifi"
+#define YOUR_WIFI_PWD "Iot@2018"
+
+static hal_uart_t sg_uart;
+
+/**
+ * @brief At send function.
+ *
+ * @return 0 for success
+ */
+static int qcloud_iot_usr_at_send(const void *data, size_t data_len)
+{
+ return tos_hal_uart_write(&sg_uart, data, data_len, 0xffffffff);
+}
+
+static int _esp8266_init(void)
+{
+ const char *at_cmd_resp[][2] = {
+ {"AT+RESTORE\r\n", "ready"},
+ {"ATE0\r\n", NULL},
+ {"AT+CWMODE=1\r\n", NULL},
+ {"AT+CIPMODE=0\r\n", NULL},
+ };
+ int rc;
+ for (int i = 0; i < sizeof(at_cmd_resp) / sizeof(const char *) / 2; i++) {
+ int retry = 3;
+ do {
+ rc = HAL_Module_SendAtCmdWaitResp(at_cmd_resp[i][0], at_cmd_resp[i][1], 5000);
+ } while (rc && retry-- > 0);
+ if (rc) {
+ break;
+ }
+ }
+ return rc;
+}
+
+/**
+ * @brief Init at module.
+ *
+ * @return 0 for success
+ */
+int HAL_Module_Init(void)
+{
+ // init at client
+ int rc = qcloud_iot_at_client_init(2048, qcloud_iot_usr_at_send);
+ if (rc < 0) {
+ Log_e("at client init fail");
+ return rc;
+ }
+ // init uart
+ rc = tos_hal_uart_init(&sg_uart, HAL_UART_PORT_2);
+ if (rc) {
+ return rc;
+ }
+
+ // init esp8266
+ return _esp8266_init();
+}
+
+/**
+ * @brief Deinit at module.
+ *
+ */
+void HAL_Module_Deinit(void)
+{
+ return;
+}
+
+/**
+ * @brief Send at cmd to at module and wait for resp.
+ *
+ * @param[in] at_cmd at cmd
+ * @param[in] at_expect expect resp
+ * @param[in] timeout_ms wait timeout
+ * @return 0 for success
+ */
+int HAL_Module_SendAtCmdWaitResp(const char *at_cmd, const char *at_expect, uint32_t timeout_ms)
+{
+ Log_d("at_cmd:%s", at_cmd);
+ return qcloud_iot_at_client_send_at_util_expect(at_cmd, at_expect, NULL, 0, timeout_ms);
+}
+
+/**
+ * @brief Send at cmd and waif for data.
+ *
+ * @param[in] at_cmd at cmd
+ * @param[in] at_expect expect resp
+ * @param[out] recv_buf recv data buffer
+ * @param[out] recv_len recv data length
+ * @param[in] timeout_ms wait timeout
+ * @return 0 for success
+ */
+int HAL_Module_SendAtCmdWaitRespWithData(const char *at_cmd, const char *at_expect, void *recv_buf, uint32_t *recv_len,
+ uint32_t timeout_ms)
+{
+ return qcloud_iot_at_client_send_at_util_expect(at_cmd, at_expect, recv_buf, recv_len, timeout_ms);
+}
+
+/**
+ * @brief Send date to at module.
+ *
+ * @param[in] data data to send
+ * @param[in] data_len data length
+ * @return 0 for success
+ */
+int HAL_Module_SendAtData(const void *data, int data_len)
+{
+ return qcloud_iot_at_client_send_data(data, data_len);
+}
+
+/**
+ * @brief Set urc.
+ *
+ * @param[in] urc irc string
+ * @param[in] urc_handler urc handler
+ * @return 0 for success
+ */
+int HAL_Module_SetUrc(const char *urc, OnUrcHandler urc_handler)
+{
+ static QcloudATUrc sg_urc_table[10];
+ static int sg_urc_table_count;
+
+ sg_urc_table[sg_urc_table_count].urc_prefix = urc;
+ sg_urc_table[sg_urc_table_count].urc_handle = urc_handler;
+ sg_urc_table_count++;
+ qcloud_iot_at_client_set_urc(sg_urc_table, sg_urc_table_count);
+ return 0;
+}
+
+/**
+ * @brief connect network
+ *
+ * @return int 0 for success
+ */
+int HAL_Module_ConnectNetwork(void)
+{
+ return HAL_Module_SendAtCmdWaitResp("AT+CWJAP=\"" YOUR_WIFI_SSID "\",\"" YOUR_WIFI_PWD "\"\r\n", "WIFI GOT IP",
+ 10000);
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/platform/os/tencentos_tiny/HAL_OS_tencentos_tiny.c b/components/connectivity/iot-hub-device-c-sdk/platform/os/tencentos_tiny/HAL_OS_tencentos_tiny.c
new file mode 100644
index 00000000..c271f38d
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/platform/os/tencentos_tiny/HAL_OS_tencentos_tiny.c
@@ -0,0 +1,373 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file HAL_OS_tencentos_tiny.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-01-24
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-01-24 1.0 fancyxu first commit
+ *
+ */
+
+#include
+#include
+#include
+
+#include "qcloud_iot_platform.h"
+#include "tos_k.h"
+
+/**
+ * @brief Sleep for ms
+ *
+ * @param[in] ms ms to sleep
+ */
+void HAL_SleepMs(uint32_t ms)
+{
+ (void)tos_sleep_hmsm(0, 0, 0, ms);
+}
+
+/**
+ * @brief Printf with format.
+ *
+ * @param[in] fmt format
+ */
+void HAL_Printf(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+
+ fflush(stdout);
+}
+
+/**
+ * @brief Snprintf with format.
+ *
+ * @param[out] str buffer to save
+ * @param[in] len buffer len
+ * @param[in] fmt format
+ * @return length of formatted string, >0 for success.
+ */
+int HAL_Snprintf(char *str, const int len, const char *fmt, ...)
+{
+ va_list args;
+ int rc;
+
+ va_start(args, fmt);
+ rc = vsnprintf(str, len, fmt, args);
+ va_end(args);
+
+ return rc;
+}
+
+/**
+ * @brief Malloc from heap.
+ *
+ * @param[in] size size to malloc
+ * @return pointer to buffer, NULL for failed.
+ */
+void *HAL_Malloc(size_t size)
+{
+ return tos_mmheap_alloc(size);
+}
+
+/**
+ * @brief Free buffer malloced by HAL_Malloc.
+ *
+ * @param[in] ptr
+ */
+void HAL_Free(void *ptr)
+{
+ tos_mmheap_free(ptr);
+}
+
+/**
+ * @brief Mutex create.
+ *
+ * @return pointer to mutex
+ */
+void *HAL_MutexCreate(void)
+{
+ k_mutex_t *mutex;
+
+ mutex = (k_mutex_t *)HAL_Malloc(sizeof(k_mutex_t));
+ if (!mutex) {
+ return K_NULL;
+ }
+
+ tos_mutex_create(mutex);
+
+ return (void *)mutex;
+}
+
+/**
+ * @brief Mutex destroy.
+ *
+ * @param[in,out] mutex pointer to mutex
+ */
+void HAL_MutexDestroy(void *mutex)
+{
+ k_err_t rc;
+
+ if (!mutex) {
+ return;
+ }
+
+ if (K_ERR_NONE != (rc = tos_mutex_destroy((k_mutex_t *)mutex))) {
+ tos_kprintf("osal_mutex_destroy err, err:%d\n\r", rc);
+ } else {
+ HAL_Free((void *)mutex);
+ }
+}
+
+/**
+ * @brief Mutex lock.
+ *
+ * @param[in,out] mutex pointer to mutex
+ */
+void HAL_MutexLock(void *mutex)
+{
+ k_err_t rc;
+
+ if (!mutex) {
+ return;
+ }
+
+ if (K_ERR_NONE != (rc = tos_mutex_pend((k_mutex_t *)mutex))) {
+ if (K_ERR_MUTEX_NESTING != rc && K_ERR_PEND_NOWAIT != rc) {
+ k_task_t *current_task = tos_task_curr_task_get();
+ tos_kprintf("%s osal_mutex_lock err, err:%d\n\r", current_task->name, rc);
+ }
+ }
+}
+
+/**
+ * @brief Mutex try lock.
+ *
+ * @param[in,out] mutex pointer to mutex
+ * @return 0 for success
+ */
+int HAL_MutexTryLock(void *mutex)
+{
+ k_err_t rc;
+ if (!mutex) {
+ return -1;
+ }
+ if (K_ERR_NONE != (rc = tos_mutex_pend_timed((k_mutex_t *)mutex, 0))) {
+ if (K_ERR_MUTEX_NESTING != rc && K_ERR_PEND_NOWAIT != rc) {
+ tos_kprintf("osal_mutex_lock err, err:%d\n\r", rc);
+ return (int)rc;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Mutex unlock.
+ *
+ * @param[in,out] mutex pointer to mutex
+ */
+void HAL_MutexUnlock(void *mutex)
+{
+ k_err_t rc;
+ if (!mutex) {
+ return;
+ }
+ if (K_ERR_NONE != (rc = tos_mutex_post((k_mutex_t *)mutex))) {
+ if (K_ERR_MUTEX_NESTING != rc && K_ERR_PEND_TIMEOUT != rc) {
+ tos_kprintf("osal_mutex_unlock err, err:%d\n\r", rc);
+ }
+ }
+}
+
+/**
+ * @brief platform-dependent thread create function
+ *
+ * @param[in,out] params params to create thread @see ThreadParams
+ * @return @see IotReturnCode
+ */
+int HAL_ThreadCreate(ThreadParams *params)
+{
+ params->thread_id = HAL_Malloc(sizeof(k_task_t));
+ if (!params->thread_id) {
+ return QCLOUD_ERR_MALLOC;
+ }
+ k_prio_t priority = TOS_CFG_TASK_PRIO_MAX - 2;
+ switch (params->priority) {
+ case THREAD_PRIORITY_HIGH:
+ priority = 2;
+ break;
+ case THREAD_PRIORITY_MIDDLE:
+ priority = TOS_CFG_TASK_PRIO_MAX / 2;
+ break;
+ case THREAD_PRIORITY_LOW:
+ priority = TOS_CFG_TASK_PRIO_MAX - 2;
+ break;
+ }
+ return tos_task_create((k_task_t *)params->thread_id, params->thread_name, (k_task_entry_t)params->thread_func,
+ params->user_arg, priority, params->stack_base, params->stack_size, 20);
+}
+
+/**
+ * @brief platform-dependent thread destroy function.
+ *
+ */
+void HAL_ThreadDestroy(void *threadId)
+{
+ // no use in sdk
+ tos_task_destroy((k_task_t *)threadId);
+ HAL_Free(threadId);
+}
+
+/**
+ * @brief platform-dependent semaphore create function.
+ *
+ * @return pointer to semaphore
+ */
+void *HAL_SemaphoreCreate(void)
+{
+ k_sem_t *sem = (k_sem_t *)malloc(sizeof(k_sem_t));
+ if (!sem) {
+ return NULL;
+ }
+
+ if (0 != tos_sem_create(sem, 0)) {
+ free(sem);
+ return NULL;
+ }
+
+ return sem;
+}
+
+/**
+ * @brief platform-dependent semaphore destory function.
+ *
+ * @param[in] sem pointer to semaphore
+ */
+void HAL_SemaphoreDestroy(void *sem)
+{
+ if (!sem) {
+ return;
+ }
+ tos_sem_destroy((k_sem_t *)sem);
+ free(sem);
+}
+
+/**
+ * @brief platform-dependent semaphore post function.
+ *
+ * @param[in] sem pointer to semaphore
+ */
+void HAL_SemaphorePost(void *sem)
+{
+ if (!sem) {
+ return;
+ }
+ tos_sem_post((k_sem_t *)sem);
+}
+
+/**
+ * @brief platform-dependent semaphore wait function.
+ *
+ * @param[in] sem pointer to semaphore
+ * @param[in] timeout_ms wait timeout
+ * @return @see IotReturnCode
+ */
+int HAL_SemaphoreWait(void *sem, uint32_t timeout_ms)
+{
+ if (!sem) {
+ return -1;
+ }
+ return tos_sem_pend((k_sem_t *)sem, timeout_ms);
+}
+
+/**
+ * @brief platform-dependent mail queue init function.
+ *
+ * @param[in] pool pool using in mail queue
+ * @param[in] mail_size mail size
+ * @param[in] mail_count mail count
+ * @return pointer to mail queue
+ */
+void *HAL_MailQueueInit(void *pool, size_t mail_size, int mail_count)
+{
+ k_mail_q_t *mail_q = HAL_Malloc(sizeof(k_mail_q_t));
+ if (!mail_q) {
+ return NULL;
+ }
+
+ int rc = tos_mail_q_create(mail_q, pool, mail_count, mail_size);
+ if (rc) {
+ HAL_Free(mail_q);
+ return NULL;
+ }
+ return mail_q;
+}
+
+/**
+ * @brief platform-dependent mail queue deinit function.
+ *
+ * @param[in] mail_q pointer to mail queue
+ */
+void HAL_MailQueueDeinit(void *mail_q)
+{
+ if (!mail_q) {
+ return;
+ }
+ tos_mail_q_destroy((k_mail_q_t *)mail_q);
+ HAL_Free(mail_q);
+ return;
+}
+
+/**
+ * @brief platform-dependent mail queue send function.
+ *
+ * @param[in] mail_q pointer to mail queue
+ * @param[in] buf data buf
+ * @param[in] size data size
+ * @return 0 for success
+ */
+int HAL_MailQueueSend(void *mail_q, const void *buf, size_t size)
+{
+ if (!mail_q) {
+ return -1;
+ }
+ return tos_mail_q_post((k_mail_q_t *)mail_q, (void *)buf, size);
+}
+
+/**
+ * @brief platform-dependent mail queue send function.
+ *
+ * @param[in] mail_q pointer to mail queue
+ * @param[out] buf data buf
+ * @param[in] size data size
+ * @param[in] timeout_ms
+ * @return 0 for success
+ */
+int HAL_MailQueueRecv(void *mail_q, void *buf, size_t *size, uint32_t timeout_ms)
+{
+ if (!mail_q) {
+ return -1;
+ }
+ return tos_mail_q_pend((k_mail_q_t *)mail_q, buf, size, timeout_ms);
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/platform/os/tencentos_tiny/HAL_TCP_tencentos_tiny.c b/components/connectivity/iot-hub-device-c-sdk/platform/os/tencentos_tiny/HAL_TCP_tencentos_tiny.c
new file mode 100644
index 00000000..23bae3ee
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/platform/os/tencentos_tiny/HAL_TCP_tencentos_tiny.c
@@ -0,0 +1,119 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file HAL_TCP_module.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-01-24
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-01-24 1.0 fancyxu first commit
+ *
+ */
+
+#include
+#include
+#include
+
+#include "qcloud_iot_common.h"
+
+#include "tos_k.h"
+#include "sal_module_wrapper.h"
+
+/**
+ * @brief TCP connect in linux
+ *
+ * @param[in] host host to connect
+ * @param[out] port port to connect
+ * @return socket fd
+ */
+int HAL_TCP_Connect(const char *host, const char *port)
+{
+ int fd;
+ Log_i("osal_tcp_connect entry, host=%s port=%d(%s)", host, port, port);
+ fd = tos_sal_module_connect(host, port, TOS_SAL_PROTO_TCP);
+ if (fd < 0) {
+ Log_i("net connect fail\n\r");
+ return -1;
+ }
+ return fd;
+}
+
+/**
+ * @brief TCP disconnect
+ *
+ * @param[in] fd socket fd
+ * @return 0 for success
+ */
+int HAL_TCP_Disconnect(int fd)
+{
+ (void)tos_sal_module_close(fd);
+ return QCLOUD_RET_SUCCESS;
+}
+
+/**
+ * @brief TCP write
+ *
+ * @param[in] fd socket fd
+ * @param[in] buf buf to write
+ * @param[in] len buf len
+ * @param[in] timeout_ms timeout
+ * @param[out] written_len data written length
+ * @return @see IotReturnCode
+ */
+int HAL_TCP_Write(int fd, const unsigned char *buf, uint32_t len, uint32_t timeout_ms, size_t *written_len)
+{
+ int ret;
+
+ ret = tos_sal_module_send(fd, buf, len);
+
+ if (ret < 0) {
+ return QCLOUD_ERR_TCP_WRITE_FAIL;
+ }
+
+ (*(int *)written_len) = ret;
+ return QCLOUD_RET_SUCCESS;
+}
+
+/**
+ * @brief TCP read.
+ *
+ * @param[in] fd socket fd
+ * @param[out] buf buffer to save read data
+ * @param[in] len buffer len
+ * @param[in] timeout_ms timeout
+ * @param[out] read_len length of data read
+ * @return @see IotReturnCode
+ */
+int HAL_TCP_Read(int fd, unsigned char *buf, uint32_t len, uint32_t timeout_ms, size_t *read_len)
+{
+ int ret;
+
+ ret = tos_sal_module_recv_timeout(fd, buf, len, timeout_ms);
+
+ if (ret < 0) {
+ return QCLOUD_ERR_TCP_READ_FAIL;
+ }
+
+ if (ret == 0) {
+ return QCLOUD_ERR_TCP_NOTHING_TO_READ;
+ }
+
+ (*(int *)read_len) = ret;
+ return QCLOUD_RET_SUCCESS;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/platform/os/tencentos_tiny/HAL_Timer_tencentos_tiny.c b/components/connectivity/iot-hub-device-c-sdk/platform/os/tencentos_tiny/HAL_Timer_tencentos_tiny.c
new file mode 100644
index 00000000..4ef83ae6
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/platform/os/tencentos_tiny/HAL_Timer_tencentos_tiny.c
@@ -0,0 +1,83 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file HAL_Timer_tencentos_tiny.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-01-24
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-01-24 1.0 fancyxu first commit
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include "qcloud_iot_platform.h"
+
+#include "tos_k.h"
+
+static uint64_t sg_time_stamp_ms;
+
+static uint64_t _tos_get_systick_ms(void)
+{
+#if (TOS_CFG_CPU_TICK_PER_SECOND == 1000)
+ return tos_systick_get();
+#else
+ k_tick_t tick = 0u;
+
+ tick = tos_systick_get() * 1000;
+ return ((tick + TOS_CFG_CPU_TICK_PER_SECOND - 1) / TOS_CFG_CPU_TICK_PER_SECOND);
+#endif
+}
+
+/**
+ * @brief Get utc time ms timestamp.
+ *
+ * @return timestamp
+ */
+uint64_t HAL_Timer_CurrentMs(void)
+{
+ return sg_time_stamp_ms + _tos_get_systick_ms();
+}
+
+/**
+ * @brief Time format string.
+ *
+ * @return time format string
+ */
+char *HAL_Timer_Current(void)
+{
+ static char time_stamp[32];
+ HAL_Snprintf(time_stamp, sizeof(time_stamp), "%llu", HAL_Timer_CurrentMs() / 1000);
+ return time_stamp;
+}
+
+/**
+ * @brief Set system time using ms timestamp
+ *
+ * @param[in] timestamp_ms
+ * @return 0 for success
+ */
+int HAL_Timer_SetSystimeMs(uint64_t timestamp_ms)
+{
+ sg_time_stamp_ms = timestamp_ms - _tos_get_systick_ms();
+ return 0;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/platform/timer/src/qcloud_iot_timer.c b/components/connectivity/iot-hub-device-c-sdk/platform/timer/src/qcloud_iot_timer.c
new file mode 100644
index 00000000..90482416
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/platform/timer/src/qcloud_iot_timer.c
@@ -0,0 +1,84 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file qcloud_iot_timer.c
+ * @brief timer interface
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-04-07
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-04-07 1.0 fancyxu first commit
+ *
+ */
+
+#include "qcloud_iot_timer.h"
+
+/**
+ * @brief Return if timer expired.
+ *
+ * @param[in] timer @see QcloudIotTimer
+ * @return true expired
+ * @return false no expired
+ */
+bool IOT_Timer_Expired(QcloudIotTimer *timer)
+{
+ return HAL_Timer_CurrentMs() > *timer ? 1 : 0;
+}
+
+/**
+ * @brief Countdown ms.
+ *
+ * @param[in,out] timer @see QcloudIotTimer
+ * @param[in] timeout_ms ms to count down
+ */
+void IOT_Timer_CountdownMs(QcloudIotTimer *timer, uint32_t timeout_ms)
+{
+ *timer = HAL_Timer_CurrentMs() + timeout_ms;
+}
+
+/**
+ * @brief Countdown second
+ *
+ * @param[in,out] timer @see QcloudIotTimer
+ * @param[in] timeout second to count down
+ */
+void IOT_Timer_Countdown(QcloudIotTimer *timer, uint32_t timeout)
+{
+ *timer = HAL_Timer_CurrentMs() + timeout * 1000;
+}
+
+/**
+ * @brief QcloudIotTimer remain ms.
+ *
+ * @param[in] timer @see QcloudIotTimer
+ * @return ms
+ */
+uint64_t IOT_Timer_Remain(QcloudIotTimer *timer)
+{
+ return *timer <= HAL_Timer_CurrentMs() ? 0 : *timer - HAL_Timer_CurrentMs();
+}
+
+/**
+ * @brief Get current utf timestamp of second
+ *
+ * @return timestamp
+ */
+uint64_t IOT_Timer_CurrentSec(void)
+{
+ return HAL_Timer_CurrentMs() / 1000;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/CMakeLists.txt b/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/CMakeLists.txt
new file mode 100644
index 00000000..9b9a65d4
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/CMakeLists.txt
@@ -0,0 +1,5 @@
+file(GLOB src_at_module ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
+set(inc_at_module ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
+
+set(src_services ${src_services} ${src_at_module} PARENT_SCOPE)
+set(inc_services ${inc_services} ${inc_at_module} PARENT_SCOPE)
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/inc/at_module.h b/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/inc/at_module.h
new file mode 100644
index 00000000..3c122fa9
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/inc/at_module.h
@@ -0,0 +1,327 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file at_mqtt_client.h
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-04-19
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-04-19 1.0 fancyxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_AT_MODULE_INC_AT_MODULE_H_
+#define IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_AT_MODULE_INC_AT_MODULE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "qcloud_iot_mqtt_client.h"
+
+/**
+ * @brief Max number of topic subscribed
+ *
+ */
+#define MAX_MESSAGE_HANDLERS (10)
+
+/**
+ * @brief Max number of ota fw version
+ *
+ */
+#define MAX_FW_VERSION_STRING (32)
+
+/**
+ * @brief Max publish length
+ *
+ */
+#define MAX_PUB_SHORT_LEN 128
+
+/**
+ * @brief Subscribe status of topic.
+ *
+ */
+typedef enum {
+ SUB_ACK_NOT_RECEIVED = 0,
+ SUB_ACK_RECEIVED = 1,
+} SubStatus;
+
+/**
+ * @brief data structure for topic subscription handle
+ *
+ */
+typedef struct {
+ char *topic_filter; /**< topic name, wildcard filter is supported */
+ SubStatus status; /**< status of sub handle */
+ SubscribeParams params; /**< params needed to subscribe */
+} SubTopicHandle;
+
+/**
+ * @brief
+ *
+ */
+typedef enum {
+ TLS_MODE_NO_TLS = 0,
+ TLS_MODE_TLS_PSK = 1,
+ TLS_MODE_TLS_CERT = 2,
+} QcloudIotTlsMode;
+
+/**
+ * @brief
+ *
+ */
+typedef enum {
+ MQTT_CONNECT_STATE_CONNECTED = 0,
+ MQTT_CONNECT_STATE_RECONNECTING,
+ MQTT_CONNECT_STATE_RECONNECTED,
+ MQTT_CONNECT_STATE_DISCONNECT,
+} MQTTConnectState;
+
+/**
+ * @brief MQTT QCloud IoT Client structure
+ *
+ */
+typedef struct {
+ DeviceInfo *device_info; /**< device info needed to connect server */
+ uint32_t command_timeout_ms; /**< MQTT command timeout, unit:ms */
+ MQTTEventHandler event_handle; /**< callback for MQTT event */
+ uint8_t default_subscribe; /**< no subscribe packet send, only add subhandle */
+ void *lock_generic; /**< mutex/lock for this client struture */
+ SubTopicHandle sub_handles[MAX_MESSAGE_HANDLERS]; /**< subscription handle array */
+ uint8_t read_buf[QCLOUD_IOT_MQTT_RX_BUF_LEN]; /**< MQTT read buffer */
+ size_t read_buf_size; /**< size of MQTT read buffer */
+ MQTTConnectState connect_state; /**< connect state */
+
+ // connect options using in module
+ QcloudIotTlsMode tls_mode; /**< 0:no tls; 1:tls+psk; 2:tls+cert */
+ uint8_t auto_connect_enable; /**< enable auto connection or not */
+ uint8_t clean_session; /**< clean session option */
+ uint16_t keep_alive_interval; /**< keepalive option */
+} QcloudIotClient;
+
+/**
+ * @brief Cls state.
+ *
+ */
+typedef enum {
+ IOT_OTA_CLS_STATE_NO_REPORT = 0,
+ IOT_OTA_CLS_STATE_REPORT_MCU,
+ IOT_OTA_CLS_STATE_REPORT_MODULE, /**< only support in at set 1.8.0 */
+ IOT_OTA_CLS_STATE_REPORT_BOTH /**< only support in at set 1.8.0 */
+} QcloudIotOtaClsState;
+
+/**
+ * @brief OTA fw info.
+ *
+ */
+typedef struct {
+ char version[MAX_FW_VERSION_STRING];
+ uint32_t fw_size;
+ uint32_t fw_max_size;
+ char md5[33];
+ void *get_fw_info_sem;
+} QcloudIotOtaFwInfo;
+
+// ----------------------------------------------------------------------------
+// cmd
+// ----------------------------------------------------------------------------
+
+/**
+ * @brief Set device info.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return 0 for success
+ */
+int module_device_info_set(const QcloudIotClient *client);
+
+/**
+ * @brief Connect mqtt server.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return 0 for success
+ */
+int module_mqtt_connect(const QcloudIotClient *client);
+
+/**
+ * @brief Disconnect mqtt server.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return 0 for success
+ */
+int module_mqtt_disconnect(const QcloudIotClient *client);
+
+/**
+ * @brief Publish mqtt message.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_name topic name to publish
+ * @param[in] params params for publish
+ * @return 0 for success
+ */
+int module_mqtt_publish(const QcloudIotClient *client, const char *topic_name, const PublishParams *params);
+
+/**
+ * @brief Subscribe mqtt topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter to subscribe
+ * @param[in] qos max qos for subscribe
+ * @return 0 for success
+ */
+int module_mqtt_subscribe(const QcloudIotClient *client, const char *topic_filter, int qos);
+
+/**
+ * @brief Unsubscribe mqtt topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter to unsubscribe
+ * @return 0 for success
+ */
+int module_mqtt_unsubscribe(const QcloudIotClient *client, const char *topic_filter);
+
+/**
+ * @brief Get mqtt connect state
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return 0 for success
+ */
+int module_mqtt_get_connect_state(const QcloudIotClient *client);
+
+/**
+ * @brief Set fw version and report.
+ *
+ * @param[in] cls_state 0: off;1: only report mcu version;2: only report module version;3: report all
+ * @param[in] version mcu fw version
+ * @return 0 for success
+ */
+int module_ota_set_fw_version(QcloudIotOtaClsState cls_state, const char *version);
+
+/**
+ * @brief Get fw info.
+ *
+ * @return 0 for success
+ */
+int module_ota_get_fw_info(void);
+
+/**
+ * @brief Read fw data.
+ *
+ * @param[out] fw_data_buf data buffer
+ * @param[out] fw_data_len data recv length
+ * @param[in] timeout read timeout
+ * @return 0 for success
+ */
+int module_ota_read_fw_data(void *fw_data_buf, uint32_t *fw_data_len, uint32_t timeout);
+
+// ----------------------------------------------------------------------------
+// urc
+// ----------------------------------------------------------------------------
+
+/**
+ * @brief Set mqtt urc to at module.
+ *
+ * @return 0 means successful.
+ */
+int module_set_mqtt_urc(void);
+
+/**
+ * @brief Set ota urc to at module.
+ *
+ * @return 0 means successful.
+ */
+int module_set_ota_urc(void);
+
+// ----------------------------------------------------------------------------
+// mqtt
+// ----------------------------------------------------------------------------
+
+/**
+ * @brief Get pointer to mqtt client.
+ *
+ * @return pointer to mqtt client.
+ */
+QcloudIotClient *qcloud_iot_get_mqtt_client(void);
+
+/**
+ * @brief Serialize and send subscribe packet.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic to subscribe
+ * @param[in] params subscribe params
+ * @return >=0 for packet id, < 0 for failed @see IotReturnCode
+ */
+int qcloud_iot_mqtt_subscribe(QcloudIotClient *client, const char *topic_filter, const SubscribeParams *params);
+
+/**
+ * @brief Serialize and send unsubscribe packet.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic to unsubscribe
+ * @return >=0 packet id, < 0 for failed @see IotReturnCode
+ */
+int qcloud_iot_mqtt_unsubscribe(QcloudIotClient *client, const char *topic_filter);
+
+/**
+ * @brief Return if topic is sub ready.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter
+ * @return true for ready
+ * @return false for not ready
+ */
+bool qcloud_iot_mqtt_is_sub_ready(QcloudIotClient *client, const char *topic_filter);
+
+/**
+ * @brief Get usr data, usr should handle lock/unlock usrdata itself in callback and caller.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter
+ * @return NULL or user data
+ */
+void *qcloud_iot_mqtt_get_subscribe_usr_data(QcloudIotClient *client, const char *topic_filter);
+
+/**
+ * @brief Clear sub handle array.
+ *
+ * @param[in,out] client pointer to mqtt client
+ */
+void qcloud_iot_mqtt_sub_handle_array_clear(QcloudIotClient *client);
+
+/**
+ * @brief Deliver message to subscribe message handler.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_name topic name, no wildcard
+ * @param[in] payload publish packet payload
+ * @param[in] payload_len payload length
+ */
+void qcloud_iot_deliver_message(QcloudIotClient *client, char *topic_name, char *payload, int payload_len);
+
+/**
+ * @brief Get ota info handle.
+ *
+ * @return @see QcloudIotOtaFwInfo
+ */
+QcloudIotOtaFwInfo *qcloud_iot_get_ota_info_handle(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_AT_MODULE_INC_AT_MODULE_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/src/at_module_cmd.c b/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/src/at_module_cmd.c
new file mode 100644
index 00000000..7180a539
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/src/at_module_cmd.c
@@ -0,0 +1,474 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file at_mqtt_cmd.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-04-19
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-04-19 1.0 fancyxu first commit
+ *
+ */
+
+#include "at_module.h"
+
+/**
+ * @brief AT Command for Iot Hub
+ * 1. network_register:
+ * AT+TCREGNET=?
+ * AT+TCREGNET?
+ * AT+TCREGNET=,[,,][,]
+ * 2. device_info:
+ * AT+TCDEVINFOSET=?
+ * AT+TCDEVINFOSET?
+ * AT+TCDEVINFOSET=,,[,][,]
+ * 3. cert:
+ * AT+TCCERTADD=?
+ * AT+TCCERTADD?
+ * AT+TCCERTADD=,
+ * AT+TCCERTCHECK=?
+ * AT+TCCERTCHECK?
+ * AT+TCCERTCHECK=
+ * AT+TCCERTDEL=?
+ * AT+TCCERTDEL?
+ * AT+TCCERTDEL=
+ * 4. dynamic register:
+ * AT+TCPRDINFOSET=?
+ * AT+TCPRDINFOSET?
+ * AT+TCPRDINFOSET=,,,
+ * AT+TCDEVREG=?
+ * AT+TCDEVREG
+ * 5. module info
+ * AT+TCMODULE
+ * AT+TCRESTORE=?
+ * 6. mqtt
+ * AT+TCMQTTCONN=?
+ * AT+TCMQTTCONN?
+ * AT+TCMQTTCONN=,,,,
+ * AT+TCMQTTDISCONN=?
+ * AT+TCMQTTDISCONN
+ * AT+TCMQTTPUB=?
+ * AT+TCMQTTPUB=,,
+ * AT+TCMQTTPUBL=?
+ * AT+TCMQTTPUBL= ,,
+ * AT+TCMQTTSUB=?
+ * AT+TCMQTTSUB?
+ * AT+TCMQTTSUB=,
+ * AT+TCMQTTUNSUB=?
+ * AT+TCMQTTUNSUB?
+ * AT+TCMQTTUNSUB=
+ * AT+TCMQTTSTATE=?
+ * AT+TCMQTTSTATE?
+ * 7. ota
+ * AT+TCOTASET=?
+ * AT+TCOTASET?
+ * AT+TCOTASET=,
+ * AT+TCFWINFO=?
+ * AT+TCFWINFO?
+ * AT+TCREADFWDATA=?
+ * AT+TCREADFWDATA=
+ *
+ */
+
+/**
+ * @brief AT module cmd.
+ *
+ */
+typedef enum {
+ MODULE_CMD_SET_DEVICE_INFO, // AT+TCDEVINFOSET=,,[,][,]
+ MODULE_CMD_CONNECT, // AT+TCMQTTCONN=,,,,
+ MODULE_CMD_DISCONNECT, // AT+TCMQTTDISCONN
+ MODULE_CMD_PUBLISH, // AT+TCMQTTPUB=,,
+ MODULE_CMD_PUBLISHL, // AT+TCMQTTPUBL=,,
+ MODULE_CMD_SUBSCRIBE, // AT+TCMQTTSUB=,
+ MODULE_CMD_UNSUBSCRIBE, // AT+TCMQTTUNSUB=
+ MODULE_CMD_GET_CONNECT_STATUS, // AT+TCMQTTSTATE?
+ MODULE_CMD_SET_OTA_VERSION, // AT+TCOTASET=,
+ MODULE_CMD_GET_OTA_FW_INFO, // AT+TCFWINFO?
+ MODULE_CMD_READ_OTA_FW_DATA // AT+TCREADFWDATA=
+} ModuleCmd;
+
+/**
+ * @brief Publish cmd params.
+ *
+ */
+typedef struct {
+ const char *topic_name;
+ const PublishParams *params;
+} ModuleCmdPublishParams;
+
+/**
+ * @brief Subscribe cmd params.
+ *
+ */
+typedef struct {
+ const char *topic_filter;
+ int qos;
+} ModuleCmdSubscribeParams;
+
+/**
+ * @brief Unsubscribe cmd params.
+ *
+ */
+typedef struct {
+ const char *topic_filter;
+} ModuleCmdUnsubscribeParams;
+
+/**
+ * @brief Set ota version to report.
+ *
+ */
+typedef struct {
+ /**
+ * @brief Control module report
+ * 0: off
+ * 1: only report mcu version
+ * 2: only report module version
+ * 3: report all
+ */
+ QcloudIotOtaClsState cls_state;
+ const char *version; /* ota version */
+} ModuleCmdSetOTAVersionParams;
+
+/**
+ * @brief Read fw data.
+ *
+ */
+typedef struct {
+ void *fw_data_buf;
+ uint32_t *fw_data_len;
+ uint32_t timeout;
+} ModuleCmdReadOTFwDataParams;
+
+/**
+ * @brief Module cmd params.
+ *
+ */
+typedef struct {
+ const QcloudIotClient *client;
+ union {
+ ModuleCmdPublishParams publish_params;
+ ModuleCmdSubscribeParams subscribe_params;
+ ModuleCmdUnsubscribeParams unsubscribe_params;
+ ModuleCmdSetOTAVersionParams set_ota_version_params;
+ ModuleCmdReadOTFwDataParams read_ota_fw_data_params;
+ };
+} ModuleCmdParams;
+
+/**
+ * @brief Transfer '\"' and ','.
+ *
+ * @param[in,out] payload payload to transfer, reuse memory
+ * @return length of payload
+ */
+static int _transfer_payload(char *payload, char **payload_transferred)
+{
+ char *tmp, *src = payload;
+ int count = 0;
+ char c = '\0';
+
+ // 1. calculate '\"' and '.'
+ while ((c = *src++) != '\0') {
+ if (c == '\"' || c == ',') {
+ count++;
+ }
+ }
+
+ // 2. malloc for payload
+ int tmp_len = src - payload + count;
+ tmp = HAL_Malloc(tmp_len);
+ if (!tmp) {
+ return 0;
+ }
+ memset(tmp, 0, tmp_len);
+
+ // 3. replace
+ for (src = payload, count = 0; (c = *src) != '\0'; src++) {
+ if (c == '\"' || c == ',') {
+ tmp[count++] = '\\';
+ }
+ tmp[count++] = c;
+ }
+
+ tmp[count] = '\0';
+ *payload_transferred = tmp;
+ return count;
+}
+
+/**
+ * @brief Send at cmd to at module.
+ *
+ * @param[in] cmd @see ModuleCmd
+ * @param[in] params @see ModuleCmdParams
+ * @return 0 for success
+ */
+static int _module_send_cmd(ModuleCmd cmd, ModuleCmdParams *params)
+{
+#define DEFAULT_TIMEOUT_MS 5000
+ int rc, len = 0;
+ char at_cmd[1250] = {0};
+ char at_resp[32] = {0};
+ char *payload_transferred = NULL;
+
+ const QcloudIotClient *client = params->client;
+
+ switch (cmd) {
+ case MODULE_CMD_SET_DEVICE_INFO:
+ strncpy(at_resp, "+TCDEVINFOSET:OK", sizeof(at_resp));
+ len = HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCDEVINFOSET=%d,\"%s\",\"%s\",\"%s\"\r\n", client->tls_mode,
+ client->device_info->product_id, client->device_info->device_name,
+ client->device_info->device_secret);
+ break;
+ case MODULE_CMD_CONNECT:
+ strncpy(at_resp, "+TCMQTTCONN:OK", sizeof(at_resp));
+ len = HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCMQTTCONN=%d,%d,%d,%d,%d\r\n", client->tls_mode,
+ client->command_timeout_ms, client->keep_alive_interval, client->clean_session,
+ client->auto_connect_enable);
+ break;
+ case MODULE_CMD_DISCONNECT:
+ len = HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCMQTTDISCONN\r\n");
+ break;
+ case MODULE_CMD_PUBLISHL:
+ len = _transfer_payload(params->publish_params.params->payload, &payload_transferred);
+ if (!len) {
+ return QCLOUD_ERR_MALLOC;
+ }
+
+ strncpy(at_resp, ">", sizeof(at_resp));
+ HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCMQTTPUBL=\"%s\",%d,%d\r\n", params->publish_params.topic_name,
+ params->publish_params.params->qos, len);
+ rc = HAL_Module_SendAtCmdWaitResp(at_cmd, at_resp, client->command_timeout_ms);
+ if (rc) {
+ HAL_Free(payload_transferred);
+ return rc;
+ }
+
+ strncpy(at_resp, "+TCMQTTPUBL:OK", sizeof(at_resp));
+ rc = HAL_Module_SendAtData(payload_transferred, len);
+ HAL_Free(payload_transferred);
+ if (rc) {
+ return rc;
+ }
+ return HAL_Module_SendAtCmdWaitResp("\r\n", at_resp, client->command_timeout_ms);
+ case MODULE_CMD_PUBLISH:
+ strncpy(at_resp, "+TCMQTTPUB:OK", sizeof(at_resp));
+ len = _transfer_payload(params->publish_params.params->payload, &payload_transferred);
+ if (!len) {
+ return QCLOUD_ERR_MALLOC;
+ }
+ len = HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCMQTTPUB=\"%s\",%d,\"%s\"\r\n",
+ params->publish_params.topic_name, params->publish_params.params->qos,
+ payload_transferred);
+ HAL_Free(payload_transferred);
+ break;
+ case MODULE_CMD_SUBSCRIBE:
+ strncpy(at_resp, "+TCMQTTSUB:OK", sizeof(at_resp));
+ len = HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCMQTTSUB=\"%s\",%d\r\n",
+ params->subscribe_params.topic_filter, params->subscribe_params.qos);
+ break;
+ case MODULE_CMD_UNSUBSCRIBE:
+ strncpy(at_resp, "+TCMQTTUNSUB:OK", sizeof(at_resp));
+ len = HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCMQTTUNSUB=\"%s\"\r\n",
+ params->subscribe_params.topic_filter);
+ break;
+ case MODULE_CMD_GET_CONNECT_STATUS:
+ len = HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCMQTTSTATE?\r\n");
+ break;
+ case MODULE_CMD_SET_OTA_VERSION:
+ strncpy(at_resp, "+TCOTASET:OK", sizeof(at_resp));
+ len = HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCOTASET=%d,\"%s\"\r\n",
+ params->set_ota_version_params.cls_state, params->set_ota_version_params.version);
+ break;
+ case MODULE_CMD_GET_OTA_FW_INFO:
+ len = HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCFWINFO?\r\n");
+ break;
+ case MODULE_CMD_READ_OTA_FW_DATA:
+ strncpy(at_resp, "+TCREADFWDATA:%u,", sizeof(at_resp));
+ HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCREADFWDATA=%d\r\n",
+ *(params->read_ota_fw_data_params.fw_data_len));
+ return HAL_Module_SendAtCmdWaitRespWithData(at_cmd, at_resp, params->read_ota_fw_data_params.fw_data_buf,
+ params->read_ota_fw_data_params.fw_data_len,
+ params->read_ota_fw_data_params.timeout);
+ default:
+ return QCLOUD_ERR_INVAL;
+ }
+ return len ? HAL_Module_SendAtCmdWaitResp(at_cmd, at_resp[0] ? at_resp : NULL,
+ client ? client->command_timeout_ms : DEFAULT_TIMEOUT_MS)
+ : QCLOUD_ERR_BUF_TOO_SHORT;
+}
+
+// ----------------------------------------------------------------------------
+// device info
+// ----------------------------------------------------------------------------
+
+/**
+ * @brief Set device info.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return 0 for success
+ */
+int module_device_info_set(const QcloudIotClient *client)
+{
+ ModuleCmdParams cmd_params = {0};
+ cmd_params.client = client;
+ return _module_send_cmd(MODULE_CMD_SET_DEVICE_INFO, &cmd_params);
+}
+
+// ----------------------------------------------------------------------------
+// mqtt
+// ----------------------------------------------------------------------------
+
+/**
+ * @brief Connect mqtt server.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return 0 for success
+ */
+int module_mqtt_connect(const QcloudIotClient *client)
+{
+ ModuleCmdParams cmd_params = {0};
+ cmd_params.client = client;
+ return _module_send_cmd(MODULE_CMD_CONNECT, &cmd_params);
+}
+
+/**
+ * @brief Disconnect mqtt server.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return 0 for success
+ */
+int module_mqtt_disconnect(const QcloudIotClient *client)
+{
+ ModuleCmdParams cmd_params = {0};
+ cmd_params.client = client;
+ return _module_send_cmd(MODULE_CMD_DISCONNECT, &cmd_params);
+}
+
+/**
+ * @brief Publish mqtt message.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_name topic name to publish
+ * @param[in] params params for publish
+ * @return 0 for success
+ */
+int module_mqtt_publish(const QcloudIotClient *client, const char *topic_name, const PublishParams *params)
+{
+ ModuleCmdParams cmd_params = {0};
+ cmd_params.client = client;
+ cmd_params.publish_params.params = params;
+ cmd_params.publish_params.topic_name = topic_name;
+ return params->payload_len > MAX_PUB_SHORT_LEN ? _module_send_cmd(MODULE_CMD_PUBLISHL, &cmd_params)
+ : _module_send_cmd(MODULE_CMD_PUBLISH, &cmd_params);
+}
+
+/**
+ * @brief Subscribe mqtt topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter to subscribe
+ * @param[in] qos max qos for subscribe
+ * @return 0 for success
+ */
+int module_mqtt_subscribe(const QcloudIotClient *client, const char *topic_filter, int qos)
+{
+ ModuleCmdParams cmd_params = {0};
+ cmd_params.client = client;
+ cmd_params.subscribe_params.topic_filter = topic_filter;
+ cmd_params.subscribe_params.qos = qos;
+ return _module_send_cmd(MODULE_CMD_SUBSCRIBE, &cmd_params);
+}
+
+/**
+ * @brief Unsubscribe mqtt topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter to unsubscribe
+ * @return 0 for success
+ */
+int module_mqtt_unsubscribe(const QcloudIotClient *client, const char *topic_filter)
+{
+ ModuleCmdParams cmd_params = {0};
+ cmd_params.client = client;
+ cmd_params.unsubscribe_params.topic_filter = topic_filter;
+ return _module_send_cmd(MODULE_CMD_UNSUBSCRIBE, &cmd_params);
+}
+
+/**
+ * @brief Get mqtt connect state
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return 0 for success
+ */
+int module_mqtt_get_connect_state(const QcloudIotClient *client)
+{
+ ModuleCmdParams cmd_params = {0};
+ cmd_params.client = client;
+ return _module_send_cmd(MODULE_CMD_GET_CONNECT_STATUS, &cmd_params);
+}
+
+// ----------------------------------------------------------------------------
+// ota
+// ----------------------------------------------------------------------------
+
+/**
+ * @brief Set fw version and report.
+ *
+ * @param[in] cls_state 0: off;1: only report mcu version;2: only report module version;3: report all
+ * @param[in] version mcu fw version
+ * @return 0 for success
+ */
+int module_ota_set_fw_version(QcloudIotOtaClsState cls_state, const char *version)
+{
+ ModuleCmdParams cmd_params = {0};
+ cmd_params.client = NULL;
+ cmd_params.set_ota_version_params.cls_state = cls_state;
+ cmd_params.set_ota_version_params.version = version;
+ return _module_send_cmd(MODULE_CMD_SET_OTA_VERSION, &cmd_params);
+}
+
+/**
+ * @brief Get fw info.
+ *
+ * @return 0 for success
+ */
+int module_ota_get_fw_info(void)
+{
+ ModuleCmdParams cmd_params = {0};
+ cmd_params.client = NULL;
+ return _module_send_cmd(MODULE_CMD_GET_OTA_FW_INFO, &cmd_params);
+}
+
+/**
+ * @brief Read fw data.
+ *
+ * @param[out] fw_data_buf data buffer
+ * @param[out] fw_data_len data recv length
+ * @param[in] timeout read timeout
+ * @return 0 for success
+ */
+int module_ota_read_fw_data(void *fw_data_buf, uint32_t *fw_data_len, uint32_t timeout)
+{
+ ModuleCmdParams cmd_params = {0};
+ cmd_params.client = NULL;
+ cmd_params.read_ota_fw_data_params.fw_data_buf = fw_data_buf;
+ cmd_params.read_ota_fw_data_params.fw_data_len = fw_data_len;
+ cmd_params.read_ota_fw_data_params.timeout = timeout;
+ return _module_send_cmd(MODULE_CMD_READ_OTA_FW_DATA, &cmd_params);
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/src/at_module_mqtt_client.c b/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/src/at_module_mqtt_client.c
new file mode 100644
index 00000000..415c3569
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/src/at_module_mqtt_client.c
@@ -0,0 +1,407 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file at_module_mqtt_client.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-04-25
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-04-25 1.0 fancyxu first commit
+ *
+ */
+
+#include "at_module.h"
+
+/**
+ * @brief Init mqtt client.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] params mqtt init params, @see MQTTInitParams
+ * @return @see IotReturnCode
+ *
+ * @note
+ * 1. init device info.
+ * 2. init params.
+ * 3. set mqtt urc to at module.
+ * 4. set device info to at module.
+ */
+static int _qcloud_iot_mqtt_client_init(QcloudIotClient *client, const MQTTInitParams *params)
+{
+ memset(client, 0x0, sizeof(QcloudIotClient));
+ client->device_info = params->device_info;
+ client->command_timeout_ms = params->command_timeout;
+ client->event_handle = params->event_handle;
+ client->default_subscribe = params->default_subscribe;
+ client->connect_state = MQTT_CONNECT_STATE_DISCONNECT;
+
+ client->tls_mode = TLS_MODE_TLS_PSK;
+ client->auto_connect_enable = params->auto_connect_enable;
+ client->clean_session = params->clean_session;
+ client->keep_alive_interval = params->keep_alive_interval > 690 ? 690 : params->keep_alive_interval;
+
+ client->lock_generic = HAL_MutexCreate();
+ if (!client->lock_generic) {
+ return QCLOUD_ERR_FAILURE;
+ }
+
+ // 1. detect module && close echo
+ int rc = HAL_Module_Init();
+ if (rc) {
+ Log_e("module init fail %d", rc);
+ return rc;
+ }
+
+ // 2. set urc function
+ rc = module_set_mqtt_urc();
+ if (rc) {
+ Log_e("set mqtt urc fail %d", rc);
+ return rc;
+ }
+
+ // 3. set device info
+ rc = module_device_info_set(client);
+ if (rc) {
+ Log_e("module init fail %d", rc);
+ return rc;
+ }
+
+ // 4. connect network
+ return HAL_Module_ConnectNetwork();
+}
+
+/**
+ * @brief Deinit mqtt client.
+ *
+ * @param[in,out] client pointer to mqtt client
+ */
+static void _qcloud_iot_mqtt_client_deinit(QcloudIotClient *client)
+{
+ qcloud_iot_mqtt_sub_handle_array_clear(client);
+ HAL_MutexDestroy(client->lock_generic);
+ HAL_Module_Deinit();
+ Log_i("release mqtt client resources");
+}
+
+// ----------------------------------------------------------------------------
+// API
+// ----------------------------------------------------------------------------
+
+/**
+ * @brief Only support one mqtt client when using at module.
+ *
+ */
+static QcloudIotClient *sg_mqtt_client;
+
+/**
+ * @brief Get pointer to mqtt client.
+ *
+ * @return pointer to mqtt client.
+ */
+QcloudIotClient *qcloud_iot_get_mqtt_client(void)
+{
+ return sg_mqtt_client;
+}
+
+/**
+ * @brief Create MQTT client and connect to MQTT server. Only one client supportted.
+ *
+ * @param[in] params MQTT init parameters
+ * @return a valid MQTT client handle when success, or NULL otherwise
+ */
+void *IOT_MQTT_Construct(const MQTTInitParams *params)
+{
+ POINTER_SANITY_CHECK(params, NULL);
+ POINTER_SANITY_CHECK(params->device_info, NULL);
+ STRING_PTR_SANITY_CHECK(params->device_info->product_id, NULL);
+ STRING_PTR_SANITY_CHECK(params->device_info->device_name, NULL);
+#ifdef AUTH_MODE_CERT
+ STRING_PTR_SANITY_CHECK(params->device_info->dev_cert_file_name, NULL);
+ STRING_PTR_SANITY_CHECK(params->device_info->dev_key_file_name, NULL);
+#else
+ STRING_PTR_SANITY_CHECK(params->device_info->device_secret, NULL);
+#endif
+ if (sg_mqtt_client) {
+ Log_e("only support one mqtt client in at module!");
+ return NULL;
+ }
+
+ int rc = 0;
+
+ QcloudIotClient *client = NULL;
+
+ // create and init MQTTClient
+ client = (QcloudIotClient *)HAL_Malloc(sizeof(QcloudIotClient));
+ if (!client) {
+ Log_e("malloc MQTTClient failed");
+ return NULL;
+ }
+
+ rc = _qcloud_iot_mqtt_client_init(client, params);
+ if (rc) {
+ Log_e("mqtt init failed: %d", rc);
+ goto exit;
+ }
+
+ if (!params->connect_when_construct) {
+ return client;
+ }
+
+ rc = IOT_MQTT_Connect(client);
+ if (rc) {
+ goto exit;
+ }
+
+ client->connect_state = MQTT_CONNECT_STATE_CONNECTED;
+ Log_i("mqtt connect with success");
+ sg_mqtt_client = client;
+ return client;
+exit:
+ _qcloud_iot_mqtt_client_deinit(client);
+ HAL_Free(client);
+ return NULL;
+}
+
+/**
+ * @brief Connect Mqtt server if not connect.
+ *
+ * @param[in,out] client pointer to mqtt client pointer, should using the pointer of IOT_MQTT_Construct return.
+ * @return @see IotReturnCode
+ */
+int IOT_MQTT_Connect(void *client)
+{
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
+ int rc = 0;
+
+ QcloudIotTimer connect_timer;
+
+ QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
+ if (mqtt_client->connect_state == MQTT_CONNECT_STATE_DISCONNECT) {
+ IOT_Timer_CountdownMs(&connect_timer, MAX_RECONNECT_WAIT_INTERVAL);
+
+ do { // wait for wifi config
+ rc = module_mqtt_connect(client);
+ if (rc) {
+ Log_e("mqtt connect with failed: %d", rc);
+ HAL_SleepMs(QCLOUD_IOT_MQTT_WAIT_ACK_TIMEOUT);
+ }
+ if (IOT_Timer_Expired(&connect_timer)) {
+ return QCLOUD_ERR_FAILURE;
+ }
+ } while (rc);
+ }
+ return QCLOUD_RET_SUCCESS;
+}
+
+/**
+ * @brief Close connection and destroy MQTT client.
+ *
+ * @param[in,out] client pointer to mqtt client pointer, should using the pointer of IOT_MQTT_Construct return.
+ * @return @see IotReturnCode
+ */
+int IOT_MQTT_Destroy(void **client)
+{
+ POINTER_SANITY_CHECK(*client, QCLOUD_ERR_INVAL);
+
+ QcloudIotClient *mqtt_client = (QcloudIotClient *)(*client);
+
+ int rc = module_mqtt_disconnect(mqtt_client);
+ _qcloud_iot_mqtt_client_deinit(mqtt_client);
+ HAL_Free(*client);
+ *client = sg_mqtt_client = NULL;
+ Log_i("mqtt release!");
+ return rc;
+}
+
+/**
+ * @brief Check connection state.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] timeout_ms timeout value (unit: ms) for this operation
+ * @return QCLOUD_RET_SUCCESS when success, others @see IotReturnCode
+ */
+int IOT_MQTT_Yield(void *client, uint32_t timeout_ms)
+{
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
+ QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
+ HAL_SleepMs(timeout_ms);
+ module_mqtt_get_connect_state(mqtt_client);
+ switch (mqtt_client->connect_state) {
+ case MQTT_CONNECT_STATE_CONNECTED:
+ return QCLOUD_RET_SUCCESS;
+ case MQTT_CONNECT_STATE_RECONNECTING:
+ return QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT;
+ case MQTT_CONNECT_STATE_RECONNECTED:
+ return QCLOUD_RET_MQTT_RECONNECTED;
+ case MQTT_CONNECT_STATE_DISCONNECT:
+ return QCLOUD_ERR_MQTT_RECONNECT_TIMEOUT;
+ }
+ return QCLOUD_RET_SUCCESS;
+}
+
+/**
+ * @brief Publish MQTT message.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_name topic to publish
+ * @param[in] params @see PublishParams
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_MQTT_Publish(void *client, const char *topic_name, const PublishParams *params)
+{
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
+ POINTER_SANITY_CHECK(params, QCLOUD_ERR_INVAL);
+ STRING_PTR_SANITY_CHECK(topic_name, QCLOUD_ERR_INVAL);
+
+ QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
+
+ if (strlen(topic_name) > MAX_SIZE_OF_CLOUD_TOPIC) {
+ return QCLOUD_ERR_MAX_TOPIC_LENGTH;
+ }
+
+ if (QOS2 == params->qos) {
+ Log_e("QoS2 is not supported currently");
+ return QCLOUD_ERR_MQTT_QOS_NOT_SUPPORT;
+ }
+
+ return module_mqtt_publish(mqtt_client, topic_name, params);
+}
+
+/**
+ * @brief Subscribe MQTT topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter to subscribe
+ * @param[in] params @see SubscribeParams
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_MQTT_Subscribe(void *client, const char *topic_filter, const SubscribeParams *params)
+{
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
+ POINTER_SANITY_CHECK(params, QCLOUD_ERR_INVAL);
+ STRING_PTR_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
+
+ QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
+
+ if (strlen(topic_filter) > MAX_SIZE_OF_CLOUD_TOPIC) {
+ return QCLOUD_ERR_MAX_TOPIC_LENGTH;
+ }
+
+ if (QOS2 == params->qos) {
+ Log_e("QoS2 is not supported currently");
+ return QCLOUD_ERR_MQTT_QOS_NOT_SUPPORT;
+ }
+
+ return qcloud_iot_mqtt_subscribe(mqtt_client, topic_filter, params);
+}
+
+/**
+ * @brief Unsubscribe MQTT topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter to unsubscribe
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_MQTT_Unsubscribe(void *client, const char *topic_filter)
+{
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
+ STRING_PTR_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
+
+ QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
+
+ if (strlen(topic_filter) > MAX_SIZE_OF_CLOUD_TOPIC) {
+ return QCLOUD_ERR_MAX_TOPIC_LENGTH;
+ }
+
+ return qcloud_iot_mqtt_unsubscribe(mqtt_client, topic_filter);
+}
+
+/**
+ * @brief Check if MQTT topic has been subscribed or not
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter to subscribe
+ * @return true already subscribed
+ * @return false not ready
+ */
+bool IOT_MQTT_IsSubReady(void *client, const char *topic_filter)
+{
+ POINTER_SANITY_CHECK(client, false);
+ STRING_PTR_SANITY_CHECK(topic_filter, false);
+ QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
+ return qcloud_iot_mqtt_is_sub_ready(mqtt_client, topic_filter);
+}
+
+/**
+ * @brief Get user data in subscribe.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter to subscribe
+ * @return NULL or user data
+ */
+void *IOT_MQTT_GetSubUsrData(void *client, const char *topic_filter)
+{
+ POINTER_SANITY_CHECK(client, NULL);
+ STRING_PTR_SANITY_CHECK(topic_filter, NULL);
+ QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
+ return qcloud_iot_mqtt_get_subscribe_usr_data(mqtt_client, topic_filter);
+}
+
+/**
+ * @brief Subscribe and wait sub ready.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter to subscribe
+ * @param[in] params @see SubscribeParams
+ * @return @see IotReturnCode
+ */
+int IOT_MQTT_SubscribeSync(void *client, const char *topic_filter, const SubscribeParams *params)
+{
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
+ POINTER_SANITY_CHECK(params, QCLOUD_ERR_INVAL);
+ STRING_PTR_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
+ return IOT_MQTT_Subscribe(client, topic_filter, params);
+}
+
+/**
+ * @brief Check if MQTT is connected.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return true connected
+ * @return false no connected
+ */
+bool IOT_MQTT_IsConnected(void *client)
+{
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
+ QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
+ module_mqtt_get_connect_state(mqtt_client);
+ return mqtt_client->connect_state != MQTT_CONNECT_STATE_DISCONNECT;
+}
+
+/**
+ * @brief Get device info using to connect mqtt server.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return @see DeviceInfo
+ */
+DeviceInfo *IOT_MQTT_GetDeviceInfo(void *client)
+{
+ POINTER_SANITY_CHECK(client, NULL);
+ QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
+ return mqtt_client->device_info;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/src/at_module_mqtt_publish.c b/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/src/at_module_mqtt_publish.c
new file mode 100644
index 00000000..db958448
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/src/at_module_mqtt_publish.c
@@ -0,0 +1,114 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file at_module_mqtt_publish.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-04-25
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-04-25 1.0 fancyxu first commit
+ *
+ */
+
+#include "at_module.h"
+
+/**
+ * @brief Check if topic match
+ *
+ * @param[in] topic_filter topic name filter, wildcard is supported
+ * @param[in] topic_name topic name, no wildcard
+ * @param[in] topic_name_len length of topic name
+ * @return 1 for matched, 0 for no matched
+ *
+ * @note assume topic filter and name is in correct format;
+ * # can only be at end;
+ * + and # can only be next to separator.
+ */
+static uint8_t _is_topic_matched(char *topic_filter, const char *topic_name, uint16_t topic_name_len)
+{
+ char *curf = topic_filter;
+ char *curn = (char *)topic_name;
+ char *curn_end = curn + topic_name_len;
+
+ while (*curf && curn < curn_end) {
+ if (*curn == '/' && *curf != '/')
+ break;
+ if (*curf != '+' && *curf != '#' && *curf != *curn)
+ break;
+ if (*curf == '+') { // skip until we meet the next separator, or end of string
+ char *nextpos = curn + 1;
+ while (nextpos < curn_end && *nextpos != '/') nextpos = ++curn + 1;
+ } else if (*curf == '#')
+ curn = curn_end - 1; // skip until end of string
+ curf++;
+ curn++;
+ };
+
+ return (curn == curn_end) && (*curf == '\0');
+}
+
+/**
+ * @brief Deliver message to subscribe message handler.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_name topic name, no wildcard
+ * @param[in] payload publish packet payload
+ * @param[in] payload_len payload length
+ */
+void qcloud_iot_deliver_message(QcloudIotClient *client, char *topic_name, char *payload, int payload_len)
+{
+ int i;
+
+ MQTTMessage message = {
+ .qos = QOS0, // no use
+ .retain = 0,
+ .dup = 0,
+ .packet_id = 0,
+ .topic_name = topic_name,
+ .topic_len = strlen(topic_name),
+ .payload_str = payload,
+ .payload_len = payload_len,
+ };
+
+ HAL_MutexLock(client->lock_generic);
+ for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
+ if (client->sub_handles[i].topic_filter &&
+ _is_topic_matched(client->sub_handles[i].topic_filter, message.topic_name, message.topic_len)) {
+ if (client->sub_handles[i].params.on_message_handler) {
+ HAL_MutexUnlock(client->lock_generic);
+ // if found, then handle it, then return
+ client->sub_handles[i].params.on_message_handler(client, &message,
+ client->sub_handles[i].params.user_data);
+ return;
+ }
+ }
+ }
+ HAL_MutexUnlock(client->lock_generic);
+
+ /* Message handler not found for topic */
+ /* May be we do not care change FAILURE use SUCCESS*/
+ Log_d("no matching any topic, call default handle function");
+
+ MQTTEventMsg msg;
+ if (client->event_handle.h_fp) {
+ msg.event_type = MQTT_EVENT_PUBLISH_RECEIVED;
+ msg.msg = &message;
+ client->event_handle.h_fp(client, client->event_handle.context, &msg);
+ }
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/src/at_module_mqtt_subscribe.c b/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/src/at_module_mqtt_subscribe.c
new file mode 100644
index 00000000..db21791f
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/src/at_module_mqtt_subscribe.c
@@ -0,0 +1,255 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file at_module_mqtt_subscribe.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-04-25
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-04-25 1.0 fancyxu first commit
+ *
+ */
+
+#include "at_module.h"
+
+/**
+ * @brief Free topic_filter and user_data
+ *
+ * @param[in] handler subtopic handle
+ */
+static void _clear_sub_handle(SubTopicHandle *handler)
+{
+ if (handler->topic_filter) {
+ HAL_Free(handler->topic_filter);
+ handler->topic_filter = NULL;
+ }
+
+ if (handler->params.user_data && handler->params.user_data_free) {
+ handler->params.user_data_free(handler->params.user_data);
+ handler->params.user_data = NULL;
+ }
+}
+
+/**
+ * @brief Remove sub handle when unsubscribe.
+ *
+ * @param[in,out] client pointer to mqtt_client
+ * @param[in] topic_filter topic to remove
+ * @return true topic exist
+ * @return false topic no exist
+ */
+static bool _remove_sub_handle_from_array(QcloudIotClient *client, const char *topic_filter)
+{
+ int i;
+ bool topic_exists = false;
+
+ // remove from message handler array
+ HAL_MutexLock(client->lock_generic);
+ for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
+ if ((client->sub_handles[i].topic_filter && !strcmp(client->sub_handles[i].topic_filter, topic_filter)) ||
+ strstr(topic_filter, "/#") || strstr(topic_filter, "/+")) {
+ // notify this event to topic subscriber
+ if (client->sub_handles[i].params.on_sub_event_handler) {
+ client->sub_handles[i].params.on_sub_event_handler(client, MQTT_EVENT_UNSUBSCRIBE,
+ client->sub_handles[i].params.user_data);
+ }
+ _clear_sub_handle(&client->sub_handles[i]);
+ // we don't want to break here, if the same topic is registered*with 2 callbacks.Unlikely scenario
+ topic_exists = true;
+ }
+ }
+ HAL_MutexUnlock(client->lock_generic);
+ return topic_exists;
+}
+
+/**
+ * @brief Add sub handle when subscribe.
+ *
+ * @param[in,out] client pointer to mqtt_client
+ * @param[in] sub_handle sub_handle to be add to array
+ * @return true topic exist
+ * @return false topic no exist
+ */
+static int _add_sub_handle_to_array(QcloudIotClient *client, const SubTopicHandle *sub_handle)
+{
+ IOT_FUNC_ENTRY;
+ int i, i_free = -1;
+
+ HAL_MutexLock(client->lock_generic);
+
+ for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
+ if (client->sub_handles[i].topic_filter) {
+ if (!strcmp(client->sub_handles[i].topic_filter, sub_handle->topic_filter)) {
+ i_free = i;
+ // free the memory before
+ _clear_sub_handle(&client->sub_handles[i]);
+ Log_w("Identical topic found: %s", sub_handle->topic_filter);
+ break;
+ }
+ } else {
+ i_free = i_free == -1 ? i : i_free;
+ }
+ }
+
+ if (-1 == i_free) {
+ Log_e("NO more @sub_handles space!");
+ HAL_MutexUnlock(client->lock_generic);
+ IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
+ }
+
+ client->sub_handles[i_free] = *sub_handle;
+ HAL_MutexUnlock(client->lock_generic);
+ IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
+}
+
+/**
+ * @brief Serialize and send subscribe packet.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic to subscribe
+ * @param[in] params subscribe params
+ * @return >=0 for packet id, < 0 for failed @see IotReturnCode
+ */
+int qcloud_iot_mqtt_subscribe(QcloudIotClient *client, const char *topic_filter, const SubscribeParams *params)
+{
+ IOT_FUNC_ENTRY;
+ int rc, qos = params->qos;
+ char *topic_filter_stored;
+ SubTopicHandle sub_handle;
+
+ // topic filter should be valid in the whole sub life
+ topic_filter_stored = HAL_Malloc(strlen(topic_filter) + 1);
+ if (!topic_filter_stored) {
+ IOT_FUNC_EXIT_RC(QCLOUD_ERR_MALLOC);
+ }
+ strncpy(topic_filter_stored, topic_filter, strlen(topic_filter) + 1);
+
+ sub_handle.topic_filter = topic_filter_stored;
+ sub_handle.params = *params;
+ sub_handle.status = client->default_subscribe ? SUB_ACK_RECEIVED : SUB_ACK_NOT_RECEIVED;
+
+ // add sub handle first to process
+ rc = _add_sub_handle_to_array(client, &sub_handle);
+ if (rc) {
+ goto exit;
+ }
+
+ if (client->default_subscribe) {
+ return 0;
+ }
+
+ rc = module_mqtt_subscribe(client, topic_filter, qos);
+ if (!rc) {
+ sub_handle.status = SUB_ACK_RECEIVED;
+ }
+ IOT_FUNC_EXIT_RC(rc);
+exit:
+ _remove_sub_handle_from_array(client, topic_filter_stored);
+ IOT_FUNC_EXIT_RC(rc);
+}
+
+/**
+ * @brief Serialize and send unsubscribe packet.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic to unsubscribe
+ * @return >=0 packet id, < 0 for failed @see IotReturnCode
+ */
+int qcloud_iot_mqtt_unsubscribe(QcloudIotClient *client, const char *topic_filter)
+{
+ IOT_FUNC_ENTRY;
+ SubTopicHandle sub_handle;
+ memset(&sub_handle, 0, sizeof(SubTopicHandle));
+
+ // remove from sub handle
+ if (!_remove_sub_handle_from_array(client, topic_filter)) {
+ Log_w("subscription does not exists: %s", topic_filter);
+ IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_UNSUB_FAIL);
+ }
+
+ IOT_FUNC_EXIT_RC(module_mqtt_unsubscribe(client, topic_filter));
+}
+
+/**
+ * @brief Return if topic is sub ready.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter
+ * @return true for ready
+ * @return false for not ready
+ */
+bool qcloud_iot_mqtt_is_sub_ready(QcloudIotClient *client, const char *topic_filter)
+{
+ IOT_FUNC_ENTRY;
+ int i = 0;
+ HAL_MutexLock(client->lock_generic);
+ for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
+ if ((client->sub_handles[i].topic_filter && !strcmp(client->sub_handles[i].topic_filter, topic_filter)) ||
+ strstr(topic_filter, "/#") || strstr(topic_filter, "/+")) {
+ HAL_MutexUnlock(client->lock_generic);
+ return client->sub_handles[i].status == SUB_ACK_RECEIVED;
+ }
+ }
+ HAL_MutexUnlock(client->lock_generic);
+ IOT_FUNC_EXIT_RC(false);
+}
+
+/**
+ * @brief Get usr data, usr should handle lock/unlock usrdata itself in callback and caller.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter
+ * @return NULL or user data
+ */
+void *qcloud_iot_mqtt_get_subscribe_usr_data(QcloudIotClient *client, const char *topic_filter)
+{
+ IOT_FUNC_ENTRY;
+ int i = 0;
+ HAL_MutexLock(client->lock_generic);
+ for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
+ if ((client->sub_handles[i].topic_filter && !strcmp(client->sub_handles[i].topic_filter, topic_filter)) ||
+ strstr(topic_filter, "/#") || strstr(topic_filter, "/+")) {
+ HAL_MutexUnlock(client->lock_generic);
+ return client->sub_handles[i].params.user_data;
+ }
+ }
+ HAL_MutexUnlock(client->lock_generic);
+ IOT_FUNC_EXIT_RC(NULL);
+}
+
+/**
+ * @brief Clear sub handle array.
+ *
+ * @param[in,out] client pointer to mqtt client
+ */
+void qcloud_iot_mqtt_sub_handle_array_clear(QcloudIotClient *client)
+{
+ IOT_FUNC_ENTRY;
+ int i;
+ for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
+ /* notify this event to topic subscriber */
+ if (client->sub_handles[i].topic_filter && client->sub_handles[i].params.on_sub_event_handler) {
+ client->sub_handles[i].params.on_sub_event_handler(client, MQTT_EVENT_CLIENT_DESTROY,
+ client->sub_handles[i].params.user_data);
+ }
+ _clear_sub_handle(&client->sub_handles[i]);
+ }
+
+ IOT_FUNC_EXIT;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/src/at_module_ota.c b/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/src/at_module_ota.c
new file mode 100644
index 00000000..98471d62
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/src/at_module_ota.c
@@ -0,0 +1,116 @@
+/**
+ * @file at_module_ota.c
+ * @author {hubert} ({hubertxxu@tencent.com})
+ * @brief
+ * @version 1.0
+ * @date 2022-06-07
+ *
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-06-07 1.0 hubertxxu first commit
+ *
+ */
+#include "at_module.h"
+
+/**
+ * @brief OTA info handle.
+ *
+ */
+static QcloudIotOtaFwInfo sg_iot_ota_info_handle;
+
+/**
+ * @brief Get ota info handle.
+ *
+ * @return @see QcloudIotOtaFwInfo
+ */
+QcloudIotOtaFwInfo *qcloud_iot_get_ota_info_handle(void)
+{
+ return &sg_iot_ota_info_handle;
+}
+
+/**
+ * @brief Init ota && report mcu & at version.
+ *
+ * @param[in] version mcu version.
+ * @return 0 for success
+ */
+int IOT_OTA_Init(const char *version)
+{
+ sg_iot_ota_info_handle.get_fw_info_sem = HAL_SemaphoreCreate();
+ if (!sg_iot_ota_info_handle.get_fw_info_sem) {
+ Log_e("create sem fail.");
+ return QCLOUD_ERR_FAILURE;
+ }
+ module_set_ota_urc();
+ // TODO: get at module version and change cls state.
+ return module_ota_set_fw_version(IOT_OTA_CLS_STATE_REPORT_MCU, version);
+}
+
+/**
+ * @brief Deinit ota.
+ *
+ */
+void IOT_OTA_Deinit(void)
+{
+ HAL_SemaphoreDestroy(sg_iot_ota_info_handle.get_fw_info_sem);
+}
+
+/**
+ * @brief Read fw info from at module.
+ *
+ * @param[out] version mcu fw version
+ * @param[out] fw_size mcu fw size
+ * @param[out] md5 mcu fw md5
+ * @param[in] timeout_ms timeout
+ * @return 0 for success
+ */
+int IOT_OTA_ReadFwInfo(char **version, uint32_t *fw_size, char **md5, uint32_t timeout_ms)
+{
+ int rc = 0;
+
+ rc = module_ota_get_fw_info();
+ if (rc) {
+ return rc;
+ }
+
+ rc = HAL_SemaphoreWait(sg_iot_ota_info_handle.get_fw_info_sem, timeout_ms);
+ if (rc) {
+ *version = NULL;
+ *fw_size = 0;
+ *md5 = NULL;
+ return QCLOUD_ERR_FAILURE;
+ }
+
+ *version = sg_iot_ota_info_handle.version;
+ *fw_size = sg_iot_ota_info_handle.fw_size;
+ *md5 = sg_iot_ota_info_handle.md5;
+ return QCLOUD_RET_SUCCESS;
+}
+
+/**
+ * @brief Read fw data from at module.
+ *
+ * @param[out] fw_data fw data
+ * @param[out] fw_data_len fw data length
+ * @param[in] timeout_ms timeout
+ * @return 0 for success
+ */
+int IOT_OTA_ReadFWData(uint8_t *fw_data, uint32_t *fw_data_len, uint32_t timeout_ms)
+{
+ return module_ota_read_fw_data(fw_data, fw_data_len, timeout_ms);
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/src/at_module_urc.c b/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/src/at_module_urc.c
new file mode 100644
index 00000000..551555b9
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/at_module/src/at_module_urc.c
@@ -0,0 +1,230 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file at_module_urc.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-04-24
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-04-24 1.0 fancyxu first commit
+ *
+ */
+
+#include "at_module.h"
+
+/**
+ * @brief URC for iot hub
+ * 1. urc for at command
+ * +TCMQTTRCVPUB
+ * +TCMQTTDISCON
+ * +TCMQTTRECONNECTING
+ * +TCMQTTRECONNECTED
+ * +TCOTASTATUS
+ * 2. urc cmd response
+ * +TCMQTTSTATE
+ * 3. urc for ota
+ * +TCREADFWDATA
+ * +TCFWINFO
+ * +TCOTASTATUS
+ *
+ */
+
+// ----------------------------------------------------------------------------
+// mqtt urc handle
+// ----------------------------------------------------------------------------
+
+/**
+ * @brief Discconect by mqtt server.
+ *
+ * @param[in] data data recv from at module
+ * @param[in] data_len data len
+ */
+static void _urc_mqtt_disconnect_handle(const char *data, size_t data_len)
+{
+ // +TCMQTTSTATE:-3
+ int error_code = 0;
+
+ QcloudIotClient *mqtt_client = qcloud_iot_get_mqtt_client();
+ if (!mqtt_client) {
+ return;
+ }
+ mqtt_client->connect_state = MQTT_CONNECT_STATE_DISCONNECT;
+
+ sscanf(data, "+TCMQTTDISCON,%d", &error_code);
+ Log_e("disconnect %d", error_code);
+}
+
+/**
+ * @brief Reconnecting.
+ *
+ * @param[in] data data recv from at module
+ * @param[in] data_len data len
+ */
+static void _urc_mqtt_reconnect_handle(const char *data, size_t data_len)
+{
+ // +TCMQTTRECONNECTING
+ QcloudIotClient *mqtt_client = qcloud_iot_get_mqtt_client();
+ if (!mqtt_client) {
+ return;
+ }
+ mqtt_client->connect_state = MQTT_CONNECT_STATE_RECONNECTING;
+}
+
+/**
+ * @brief Reconnected.
+ *
+ * @param[in] data data recv from at module
+ * @param[in] data_len data len
+ */
+static void _urc_mqtt_reconnected_handle(const char *data, size_t data_len)
+{
+ // +TCMQTTRECONNECTED
+ QcloudIotClient *mqtt_client = qcloud_iot_get_mqtt_client();
+ if (!mqtt_client) {
+ return;
+ }
+ mqtt_client->connect_state = MQTT_CONNECT_STATE_RECONNECTED;
+}
+
+/**
+ * @brief Connect state.
+ *
+ * @param[in] data data recv from at module
+ * @param[in] data_len data len
+ */
+static void _urc_mqtt_state_handle(const char *data, size_t data_len)
+{
+ // +TCMQTTSTATE:1
+ int connect_state = 1;
+
+ QcloudIotClient *mqtt_client = qcloud_iot_get_mqtt_client();
+ if (!mqtt_client) {
+ return;
+ }
+ sscanf(data, "+TCMQTTSTATE:%d", &connect_state);
+ Log_d("mqtt state %d", connect_state);
+ if (connect_state) {
+ mqtt_client->connect_state = MQTT_CONNECT_STATE_CONNECTED;
+ }
+}
+
+/**
+ * @brief Recv publish packet.
+ *
+ * @param[in] data data recv from at module
+ * @param[in] data_len data len
+ */
+static void _urc_mqtt_publish_handle(const char *data, size_t data_len)
+{
+ // +TCMQTTRCVPUB:"$thing/down/property/Q5NNWVC2Z8/test1",81,"{"method":"report_reply","clientToken":"property-69","code":0,"status":"success"}"
+
+ QcloudIotClient *mqtt_client = qcloud_iot_get_mqtt_client();
+ if (!mqtt_client) {
+ return;
+ }
+
+ char *head, *topic_name, *payload;
+ char *src = (char *)data;
+
+ src[data_len - 1] = '\0';
+
+ head = strtok((char *)data, "\"");
+ if (!head) {
+ return;
+ }
+
+ topic_name = strtok(NULL, "\"");
+ if (!topic_name) {
+ return;
+ }
+
+ strtok(NULL, "\"");
+ payload = strtok(NULL, "");
+ if (!payload) {
+ return;
+ }
+ qcloud_iot_deliver_message(mqtt_client, topic_name, payload, strlen(payload));
+}
+
+// ----------------------------------------------------------------------------
+// ota urc handle
+// ----------------------------------------------------------------------------
+
+static void _delete_char(char *str, char ch)
+{
+ char *p;
+ for (p = str; *p != '\0'; p++)
+ if (*p != ch)
+ *str++ = *p;
+ *str = '\0';
+}
+
+/**
+ * @brief Recv fw info.
+ *
+ * @param[in] data data recv from at module
+ * @param[in] data_len data len
+ */
+static void _urc_ota_fw_info_handle(const char *data, size_t data_len)
+{
+ // +TCFWINFO:"1.2.0",17300,"a2aa3c261ebfc1322edafd37edb6b183",262144
+ QcloudIotOtaFwInfo *ota_info = qcloud_iot_get_ota_info_handle();
+ _delete_char((char *)data, '"');
+ sscanf((char *)data, "+TCFWINFO:%[^,],%u,%[^,],%u", ota_info->version, &(ota_info->fw_size), ota_info->md5,
+ &ota_info->fw_max_size);
+ HAL_SemaphorePost(ota_info->get_fw_info_sem);
+}
+
+static void _urc_ota_state_handle(const char *data, size_t data_len)
+{
+ // Todo
+}
+
+// ----------------------------------------------------------------------------
+// API
+// ----------------------------------------------------------------------------
+
+/**
+ * @brief Set mqtt urc to at module.
+ *
+ * @return 0 means successful.
+ */
+int module_set_mqtt_urc(void)
+{
+ int rc;
+ rc = HAL_Module_SetUrc("+TCMQTTRCVPUB", _urc_mqtt_publish_handle);
+ rc |= HAL_Module_SetUrc("+TCMQTTDISCON", _urc_mqtt_disconnect_handle);
+ rc |= HAL_Module_SetUrc("+TCMQTTRECONNECTING", _urc_mqtt_reconnect_handle);
+ rc |= HAL_Module_SetUrc("+TCMQTTRECONNECTED", _urc_mqtt_reconnected_handle);
+ rc |= HAL_Module_SetUrc("+TCMQTTSTATE", _urc_mqtt_state_handle);
+ return rc;
+}
+
+/**
+ * @brief Set ota urc to at module.
+ *
+ * @return 0 means successful.
+ */
+int module_set_ota_urc(void)
+{
+ int rc;
+ rc = HAL_Module_SetUrc("+TCFWINFO", _urc_ota_fw_info_handle);
+ rc |= HAL_Module_SetUrc("+TCOTASTATUS", _urc_ota_state_handle);
+ return rc;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/cos/CMakeLists.txt b/components/connectivity/iot-hub-device-c-sdk/services/common/cos/CMakeLists.txt
new file mode 100644
index 00000000..37c62f95
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/cos/CMakeLists.txt
@@ -0,0 +1,8 @@
+file(GLOB src_cos ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
+set(src_services ${src_services} ${src_cos} PARENT_SCOPE)
+
+if( ${CONFIG_COMPILE_SAMPLE} STREQUAL "ON")
+ file(GLOB src_cos_download_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/cos_download_sample.c)
+ add_executable(cos_download_sample ${src_cos_download_sample})
+ target_link_libraries(cos_download_sample ${libsdk})
+endif()
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/cos/sample/cos_download_sample.c b/components/connectivity/iot-hub-device-c-sdk/services/common/cos/sample/cos_download_sample.c
new file mode 100644
index 00000000..42b62df3
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/cos/sample/cos_download_sample.c
@@ -0,0 +1,93 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file cos_download_sample.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-10-27
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-10-27 1.0 fancyxu first commit
+ *
+ */
+
+#include
+#include
+#include
+#include
+
+#include "qcloud_iot_cos.h"
+
+#include "utils_log.h"
+
+// ----------------------------------------------------------------------------
+// Main
+// ----------------------------------------------------------------------------
+static int sg_main_exit = 0;
+
+#ifdef __linux__
+
+#include
+#include
+#include
+
+static void _main_exit(int sig)
+{
+ Log_e("demo exit by signal:%d\n", sig);
+ sg_main_exit = 1;
+}
+#endif
+
+int main(int argc, char **argv)
+{
+#ifdef __linux__
+ signal(SIGINT, _main_exit);
+#endif
+
+ int rc = 0;
+
+ // init log level
+ LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
+ utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
+
+ IotCosDownloadParams connect_params = {
+ .url = "http://localhost", // your cos url
+ .file_size = 0, // your cos file size
+ .offset = 0,
+ .is_fragmentation = false,
+ .is_https_enabled = false,
+ };
+
+ uint8_t buf[1024];
+
+ void *handle = IOT_COS_DownloadInit(&connect_params);
+ if (!handle) {
+ goto exit;
+ }
+
+ while (!IOT_COS_DownloadIsFinished(handle)) {
+ rc = IOT_COS_DownloadFetch(handle, buf, sizeof(buf), 5000);
+ if (rc < 0) {
+ break;
+ }
+ }
+ IOT_COS_DownloadDeinit(handle);
+exit:
+ utils_log_deinit();
+ return rc;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/cos/src/cos_download.c b/components/connectivity/iot-hub-device-c-sdk/services/common/cos/src/cos_download.c
new file mode 100644
index 00000000..45ecc721
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/cos/src/cos_download.c
@@ -0,0 +1,261 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file cos_download.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-10-25
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-10-25 1.0 fancyxu first commit
+ *
+ */
+
+#include "qcloud_iot_cos.h"
+
+/**
+ * @brief COS request download header.
+ *
+ */
+#define HTTP_COS_DOWNLOAD_REQUEST_HEADER_LEN 256
+
+/**
+ * @brief Download handle.
+ *
+ */
+typedef struct {
+ IotCosDownloadParams params;
+ void *http_client;
+ IotHTTPRequestParams http_request;
+ int download_size;
+ int is_first_run;
+} HTTPCosDownloadHandle;
+
+/**
+ * @brief Connect cos http server.
+ *
+ * @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
+ * @return 0 for success. others @see IotReturnCode
+ */
+static int _cos_download_connect(HTTPCosDownloadHandle *handle)
+{
+ IotHTTPConnectParams connect_params = {
+ .url = handle->params.url,
+ .port = handle->params.is_https_enabled ? "443" : "80",
+ .ca_crt = NULL, // TODO: support cert
+
+ };
+ return IOT_HTTP_Connect(handle->http_client, &connect_params);
+}
+
+/**
+ * @brief Construct cos download request header.
+ *
+ * @param[out] header pointer to request header
+ * @param[in] is_fragmentation http fragmentation
+ * @param[in] begin_byte download begin byte
+ * @param[in] end_byte download end byte
+ * @return > 0 for header len, others fail
+ */
+static int _cos_download_request_header_construct(char *header, int is_fragmentation, int begin_byte, int end_byte)
+{
+ int len = HAL_Snprintf(header, HTTP_COS_DOWNLOAD_REQUEST_HEADER_LEN,
+ "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
+ "Accept-Encoding:gzip, deflate\r\nRange:bytes=%d-%d\r\n",
+ begin_byte, end_byte);
+ if (len <= 0) {
+ return QCLOUD_ERR_BUF_TOO_SHORT;
+ }
+
+ if (is_fragmentation) {
+ const char *keep_alive = "Connection:keep-alive\r\n";
+ int keep_alive_len = strlen(keep_alive);
+ if (HTTP_COS_DOWNLOAD_REQUEST_HEADER_LEN - len - 1 <= keep_alive_len) {
+ return QCLOUD_ERR_BUF_TOO_SHORT;
+ }
+ strncpy(header + len, keep_alive, keep_alive_len);
+ header[len + keep_alive_len] = '\0';
+ len += keep_alive_len;
+ }
+ return len;
+}
+
+/**
+ * @brief Request cos download.
+ *
+ * @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
+ * @param[in] max_len max_len for buffer to download in fragmentation
+ * @return 0 for success. others @see IotReturnCode
+ */
+static int _cos_download_request(HTTPCosDownloadHandle *handle, int max_len)
+{
+ int rc, begin_byte, end_byte = 0;
+
+ begin_byte = handle->download_size;
+#define min_http(x, y) (((x) < (y)) ? (x) : (y))
+ end_byte = handle->params.is_fragmentation
+ ? begin_byte + min_http(max_len, handle->params.file_size - handle->download_size) - 1
+ : handle->params.file_size - 1;
+#undef min_http
+
+ rc = _cos_download_request_header_construct(handle->http_request.header, handle->params.is_fragmentation,
+ begin_byte, end_byte);
+ if (rc <= 0) {
+ return rc;
+ }
+
+ handle->http_request.url = handle->params.url;
+ handle->http_request.method = IOT_HTTP_METHOD_GET;
+ handle->http_request.content_length = 0;
+ handle->http_request.content = handle->http_request.content_type = NULL;
+ return IOT_HTTP_Request(handle->http_client, &handle->http_request);
+}
+
+/**
+ * @brief HTTP recv data.
+ *
+ * @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
+ * @param[out] buf buf to store received data
+ * @param[in] buf_len buf_len
+ * @param[in] timeout_ms read socket timeout
+ * @return >= 0 for recv data len. others @see IotReturnCode
+ */
+static int _cos_download_recv_data(HTTPCosDownloadHandle *handle, uint8_t *buf, int buf_len, uint32_t timeout_ms)
+{
+ int rc = IOT_HTTP_Recv(handle->http_client, buf, buf_len, timeout_ms);
+ if (rc > 0) {
+ handle->download_size += rc;
+ }
+ return rc;
+}
+
+/**************************************************************************************
+ * API
+ **************************************************************************************/
+
+/**
+ * @brief Init cos download handle.
+ *
+ * @param[in] params @see IotCosDownloadParams
+ * @return pointer to cos download handle
+ */
+void *IOT_COS_DownloadInit(IotCosDownloadParams *params)
+{
+ POINTER_SANITY_CHECK(params, NULL);
+
+ HTTPCosDownloadHandle *handle = HAL_Malloc(sizeof(HTTPCosDownloadHandle));
+ if (!handle) {
+ goto exit;
+ }
+
+ handle->http_client = IOT_HTTP_Init();
+ if (!handle->http_client) {
+ goto exit;
+ }
+
+ handle->http_request.header = HAL_Malloc(HTTP_COS_DOWNLOAD_REQUEST_HEADER_LEN);
+ if (!handle->http_request.header) {
+ goto exit;
+ }
+
+ handle->params = *params;
+ handle->download_size = params->offset;
+
+ if (_cos_download_connect(handle)) {
+ goto exit;
+ }
+ handle->is_first_run = 1;
+ return handle;
+exit:
+ if (handle) {
+ HAL_Free(handle->http_request.header);
+ IOT_HTTP_Deinit(handle->http_client);
+ HAL_Free(handle);
+ }
+ return NULL;
+}
+
+/**
+ * @brief Fetch data from cos.
+ *
+ * @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
+ * @param[out] buf buffer to store data
+ * @param[in] buf_len buffer length
+ * @param timeout_ms timeout for fetching
+ * @return >= 0 for recv data len. others @see IotReturnCode
+ */
+int IOT_COS_DownloadFetch(void *handle, uint8_t *buf, uint32_t buf_len, uint32_t timeout_ms)
+{
+ POINTER_SANITY_CHECK(handle, QCLOUD_ERR_INVAL);
+ int rc = 0;
+
+ HTTPCosDownloadHandle *download_handle = (HTTPCosDownloadHandle *)handle;
+
+ // download finish
+ if (IOT_COS_DownloadIsFinished(handle)) {
+ return 0;
+ }
+
+ if (download_handle->is_first_run) {
+ rc = _cos_download_request(download_handle, buf_len);
+ if (rc) {
+ Log_e("cos request failed %d", rc);
+ return rc;
+ }
+ download_handle->is_first_run = 0;
+ return _cos_download_recv_data(download_handle, buf, buf_len, timeout_ms);
+ }
+
+ if (download_handle->params.is_fragmentation && IOT_HTTP_IsRecvFinished(download_handle->http_client)) {
+ rc = _cos_download_request(download_handle, buf_len);
+ if (rc) {
+ Log_e("cos request failed %d", rc);
+ return rc;
+ }
+ }
+
+ return _cos_download_recv_data(download_handle, buf, buf_len, timeout_ms);
+}
+
+/**
+ * @brief Is download finished.
+ *
+ * @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
+ * @return true for finished
+ */
+int IOT_COS_DownloadIsFinished(void *handle)
+{
+ POINTER_SANITY_CHECK(handle, QCLOUD_ERR_INVAL);
+ HTTPCosDownloadHandle *download_handle = (HTTPCosDownloadHandle *)handle;
+ return download_handle->download_size == download_handle->params.file_size;
+}
+
+/**
+ * @brief Deinit cos download.
+ *
+ * @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
+ */
+void IOT_COS_DownloadDeinit(void *handle)
+{
+ POINTER_SANITY_CHECK_RTN(handle);
+ HTTPCosDownloadHandle *download_handle = (HTTPCosDownloadHandle *)handle;
+ IOT_HTTP_Disconnect(download_handle->http_client);
+ IOT_HTTP_Deinit(download_handle->http_client);
+ HAL_Free(download_handle->http_request.header);
+ HAL_Free(download_handle);
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/dynreg/CMakeLists.txt b/components/connectivity/iot-hub-device-c-sdk/services/common/dynreg/CMakeLists.txt
new file mode 100644
index 00000000..f94b526b
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/dynreg/CMakeLists.txt
@@ -0,0 +1,13 @@
+file(GLOB src_dynreg ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
+set(src_services ${src_services} ${src_dynreg} PARENT_SCOPE)
+
+if( ${CONFIG_COMPILE_SAMPLE} STREQUAL "ON")
+ file(GLOB src_dynreg_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/dynreg_sample.c)
+ add_executable(dynreg_sample ${src_dynreg_sample})
+ target_link_libraries(dynreg_sample ${libsdk})
+endif()
+
+if( ${CONFIG_IOT_TEST} STREQUAL "ON")
+ file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
+ set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
+endif()
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/dynreg/sample/dynreg_sample.c b/components/connectivity/iot-hub-device-c-sdk/services/common/dynreg/sample/dynreg_sample.c
new file mode 100644
index 00000000..e70d5a59
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/dynreg/sample/dynreg_sample.c
@@ -0,0 +1,88 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file cos_download_sample.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-10-27
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-10-27 1.0 fancyxu first commit
+ *
+ */
+
+#include
+#include
+#include
+#include
+
+#include "qcloud_iot_common.h"
+#include "utils_log.h"
+
+// ----------------------------------------------------------------------------
+// Main
+// ----------------------------------------------------------------------------
+static int sg_main_exit = 0;
+
+#ifdef __linux__
+
+#include
+#include
+#include
+
+static void _main_exit(int sig)
+{
+ Log_e("demo exit by signal:%d\n", sig);
+ sg_main_exit = 1;
+}
+#endif
+
+int main(int argc, char **argv)
+{
+#ifdef __linux__
+ signal(SIGINT, _main_exit);
+#endif
+
+ int rc = 0;
+
+ // init log level
+ LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
+ utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
+
+ DeviceInfo device_info;
+ // 1. get device info
+ rc = HAL_GetDevInfo((void *)&device_info);
+ if (rc) {
+ Log_e("get device info failed: %d", rc);
+ return rc;
+ }
+ // 2. do dynreg
+ rc = IOT_DynReg_Device(&device_info);
+ if (rc) {
+ Log_e("dynreg error");
+ goto exit;
+ }
+
+ // 3. get device secret just save it. then you can do mqtt
+ HAL_SetDevInfo(&device_info);
+ Log_i("device secret : %s", device_info.device_secret);
+
+exit:
+ utils_log_deinit();
+ return rc;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/dynreg/src/dynreg.c b/components/connectivity/iot-hub-device-c-sdk/services/common/dynreg/src/dynreg.c
new file mode 100644
index 00000000..642af05e
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/dynreg/src/dynreg.c
@@ -0,0 +1,161 @@
+/**
+ * @file dynreg.c
+ * @author {hubert} ({hubertxxu@tencent.com})
+ * @brief
+ * @version 1.0
+ * @date 2022-01-26
+ *
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-01-26 1.0 hubertxxu first commit
+ *
+ */
+
+#include "qcloud_iot_common.h"
+#include "utils_json.h"
+#include "utils_base64.h"
+#include "mbedtls/aes.h"
+
+#define DYN_RESPONSE_BUFF_LEN (256)
+#define DECODE_BUFF_LEN (256)
+#define UTILS_AES_BLOCK_LEN (16)
+
+/**
+ * @brief aes cbc handle
+ *
+ * @return int 0 is success other is error.
+ */
+static int _aes_cbc(uint8_t *input_data, uint32_t input_data_len, uint8_t *output_data, uint32_t output_data_len,
+ DeviceInfo *device_info)
+{
+ int i;
+ uint8_t iv[16] = {0};
+ char key[UTILS_AES_BLOCK_LEN + 1] = {0};
+ for (i = 0; i < 16; i++) {
+ iv[i] = '0';
+ }
+ strncpy(key, device_info->product_secret, UTILS_AES_BLOCK_LEN);
+ mbedtls_aes_context aes_ctx;
+ mbedtls_aes_init(&aes_ctx);
+ mbedtls_aes_setkey_dec(&aes_ctx, (const unsigned char *)key, 128);
+ if (mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, input_data_len, iv, (unsigned char *)input_data,
+ (unsigned char *)output_data)) {
+ return QCLOUD_ERR_FAILURE;
+ }
+ mbedtls_aes_free(&aes_ctx);
+ return QCLOUD_RET_SUCCESS;
+}
+
+/**
+ * @brief parse device register response buffer
+ *
+ * @param response_buf
+ * @param response_len
+ * @return int 0 success other error
+ */
+static int _parse_response_result(char *response_buf, int response_len, DeviceInfo *device_info)
+{
+ int rc = 0;
+ UtilsJsonValue value;
+ char base64_decode_buf[DECODE_BUFF_LEN] = {0};
+ size_t olen = 0;
+
+ // 1. find payload
+ rc = utils_json_value_get("Response.Payload", strlen("Response.Payload"), response_buf, response_len, &value);
+ if (rc) {
+ Log_e("can not find payload. json : %s", response_buf);
+ goto exit;
+ }
+
+ // 2. base64 decode
+ rc = utils_base64decode((uint8_t *)base64_decode_buf, DECODE_BUFF_LEN, &olen, (const uint8_t *)value.value,
+ value.value_len);
+ if (rc) {
+ Log_e("base64 decode error.");
+ goto exit;
+ }
+
+ // 3. aes cbc
+ olen = olen + (16 - olen % 16);
+ rc = _aes_cbc((uint8_t *)base64_decode_buf, (uint32_t)olen, (uint8_t *)base64_decode_buf, DECODE_BUFF_LEN,
+ device_info);
+ if (rc) {
+ Log_e("aes cbc error. ");
+ goto exit;
+ }
+
+ // 4. find psk
+ uint32_t encryption_type = 0;
+ rc = utils_json_get_uint32("encryptionType", strlen("encryptionType"), base64_decode_buf, DECODE_BUFF_LEN,
+ &encryption_type);
+ if (rc) {
+ Log_e("parse encryptionType error. json : %s", base64_decode_buf);
+ goto exit;
+ }
+ rc = utils_json_value_get("psk", strlen("psk"), base64_decode_buf, DECODE_BUFF_LEN, &value);
+ if (rc) {
+ Log_e("parse psk error. json : %s", base64_decode_buf);
+ goto exit;
+ }
+
+ // 5. copy psk to device info
+ strncpy(device_info->device_secret, value.value, value.value_len);
+
+exit:
+ return rc;
+}
+
+/**
+ * @brief dynreg device, get device secret or device cert file and private key file from iot platform
+ *
+ * @param[in] params @see DeviceInfo
+ * @return 0 is success other is failed
+ */
+int IOT_DynReg_Device(DeviceInfo *device_info)
+{
+ int rc = 0;
+ POINTER_SANITY_CHECK(device_info, QCLOUD_ERR_INVAL);
+
+ if (strlen(device_info->product_secret) < UTILS_AES_BLOCK_LEN) {
+ Log_e("product key illegal");
+ rc = QCLOUD_ERR_FAILURE;
+ goto exit;
+ }
+
+ char dynreg_buf[DYN_RESPONSE_BUFF_LEN] = {0};
+
+ /* constructor dynreg http body */
+ int request_body_len =
+ HAL_Snprintf(dynreg_buf, DYN_RESPONSE_BUFF_LEN, "{\"ProductId\":\"%s\",\"DeviceName\":\"%s\"}",
+ device_info->product_id, device_info->device_name);
+
+ HttpSignedParams params = {
+ .host = DYN_REG_SERVER_URL,
+ .uri = DYN_REG_URI_PATH,
+ .need_recv = true,
+ .recv_timeout_ms = 2000,
+ .secretkey = device_info->product_secret,
+ };
+ rc = IOT_HTTP_SignedRequest(¶ms, dynreg_buf, request_body_len, (uint8_t *)dynreg_buf, DYN_RESPONSE_BUFF_LEN);
+ if (rc < 0) {
+ goto exit;
+ }
+ rc = _parse_response_result(dynreg_buf, rc, device_info);
+exit:
+ return rc;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/dynreg/test/.clang-format b/components/connectivity/iot-hub-device-c-sdk/services/common/dynreg/test/.clang-format
new file mode 100644
index 00000000..334c1c0d
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/dynreg/test/.clang-format
@@ -0,0 +1,8 @@
+Language: Cpp
+BasedOnStyle: Google
+ColumnLimit: 120
+DerivePointerAlignment: true
+PointerAlignment: Left
+SortIncludes: true
+IncludeBlocks: Preserve
+IndentPPDirectives: AfterHash
\ No newline at end of file
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/dynreg/test/test_dynreg.cc b/components/connectivity/iot-hub-device-c-sdk/services/common/dynreg/test/test_dynreg.cc
new file mode 100644
index 00000000..45b387b1
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/dynreg/test/test_dynreg.cc
@@ -0,0 +1,42 @@
+/**
+ * @file test_dynreg.cc
+ * @author {hubert} ({hubertxxu@tencent.com})
+ * @brief
+ * @version 1.0
+ * @date 2022-01-26
+ *
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-01-26 1.0 hubertxxu first commit
+ *
+ */
+
+#include "gtest/gtest.h"
+#include "mqtt_client_test.h"
+#include "qcloud_iot_common.h"
+
+namespace mqtt_client_unittest {
+
+TEST_F(MqttClientTest, dynreg_test) {
+ DeviceInfo device_info;
+ ASSERT_EQ(HAL_GetDevInfo(&device_info), 0);
+ strncpy(device_info.device_name, "dynreg_test", 12);
+ EXPECT_EQ(IOT_DynReg_Device(&device_info), 0);
+}
+
+} // namespace mqtt_client_unittest
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/CMakeLists.txt b/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/CMakeLists.txt
new file mode 100644
index 00000000..4ce87ac4
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/CMakeLists.txt
@@ -0,0 +1,15 @@
+file(GLOB src_gateway ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
+set(src_services ${src_services} ${src_gateway} PARENT_SCOPE)
+
+if( ${CONFIG_COMPILE_SAMPLE} STREQUAL "ON")
+ file(GLOB src_gateway_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/*.c)
+ add_executable(gateway_sample ${src_gateway_sample})
+ target_link_libraries(gateway_sample ${libsdk})
+endif()
+
+if( ${CONFIG_IOT_TEST} STREQUAL "ON")
+ file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
+ set(inc_gateway_test ${CMAKE_CURRENT_SOURCE_DIR}/test)
+ set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
+ set(inc_test ${inc_test} ${inc_gateway_test} PARENT_SCOPE)
+endif()
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/sample/gateway_sample.c b/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/sample/gateway_sample.c
new file mode 100644
index 00000000..6360b965
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/sample/gateway_sample.c
@@ -0,0 +1,312 @@
+/**
+ * @file gateway_sample.c
+ * @author {hubert} ({hubertxxu@tencent.com})
+ * @brief
+ * @version 1.0
+ * @date 2022-05-25
+ *
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-05-25 1.0 hubertxxu first commit
+ *
+ */
+
+#include
+#include
+#include
+#include
+
+#include "qcloud_iot_common.h"
+#include "qcloud_iot_explorer.h"
+
+#include "utils_log.h"
+
+#include "gateway_subdev_handle.h"
+
+/**
+ * @brief MQTT event callback, @see MQTTEventHandleFun
+ *
+ * @param[in] client pointer to mqtt client
+ * @param[in] handle_context context
+ * @param[in] msg msg
+ */
+static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
+{
+ MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
+ uintptr_t packet_id = (uintptr_t)msg->msg;
+
+ switch (msg->event_type) {
+ case MQTT_EVENT_UNDEF:
+ Log_i("undefined event occur.");
+ break;
+
+ case MQTT_EVENT_DISCONNECT:
+ Log_i("MQTT disconnect.");
+ break;
+
+ case MQTT_EVENT_RECONNECT:
+ Log_i("MQTT reconnect.");
+ break;
+
+ case MQTT_EVENT_PUBLISH_RECEIVED:
+ Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
+ mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
+ mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
+ break;
+
+ case MQTT_EVENT_SUBSCRIBE_SUCCESS:
+ Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
+ Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_SUBSCRIBE_NACK:
+ Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
+ Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
+ Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_UNSUBSCRIBE_NACK:
+ Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_PUBLISH_SUCCESS:
+ Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_PUBLISH_TIMEOUT:
+ Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_PUBLISH_NACK:
+ Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ default:
+ Log_i("Should NOT arrive here.");
+ break;
+ }
+}
+
+/**
+ * @brief Setup MQTT construct parameters.
+ *
+ * @param[in,out] initParams @see MQTTInitParams
+ * @param[in] device_info @see DeviceInfo
+ */
+static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
+{
+ init_params->device_info = device_info;
+ init_params->event_handle.h_fp = _mqtt_event_handler;
+}
+
+// ----------------------------------------------------------------------------
+// Data template callback
+// ----------------------------------------------------------------------------
+#if 0
+static void _method_control_callback(UtilsJsonValue client_token, UtilsJsonValue params, void *usr_data)
+{
+ char buf[256];
+ Log_i("recv msg[%.*s]: params=%.*s", client_token.value_len, client_token.value, params.value_len, params.value);
+ IOT_DataTemplate_PropertyControlReply(usr_data, buf, sizeof(buf), 0, client_token);
+}
+
+static void _method_report_reply_callback(UtilsJsonValue client_token, int code, void *usr_data)
+{
+ Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
+}
+
+static void _method_get_status_reply_callback(UtilsJsonValue client_token, int code, UtilsJsonValue reported,
+ UtilsJsonValue control, void *usr_data)
+{
+ char buf[256];
+ Log_i("recv msg[%.*s]: code=%d|reported=%.*s|control=%.*s", client_token.value_len, client_token.value, code,
+ reported.value_len, STRING_PTR_PRINT_SANITY_CHECK(reported.value), control.value_len,
+ STRING_PTR_PRINT_SANITY_CHECK(control.value));
+ IOT_DataTemplate_PropertyClearControl(usr_data, buf, sizeof(buf));
+}
+
+static void _method_report_info_reply_callback(UtilsJsonValue client_token, int code, void *usr_data)
+{
+ Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
+}
+
+static void _method_clear_control_reply_callback(UtilsJsonValue client_token, int code, void *usr_data)
+{
+ Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
+}
+
+static void _method_event_reply_callback(UtilsJsonValue client_token, int code, void *usr_data)
+{
+ Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
+}
+
+static void _method_action_callback(UtilsJsonValue client_token, UtilsJsonValue action_id, UtilsJsonValue params,
+ void *usr_data)
+{
+ char buf[256];
+
+ Log_i("recv msg[%.*s]: action_id=%.*s|params=%.*s", client_token.value_len, client_token.value, action_id.value_len,
+ action_id.value, params.value_len, params.value);
+ IotDataTemplateActionReply reply = {
+ .code = 0,
+ .client_token = client_token,
+ .response = "{\"err_code\":0}",
+ };
+ IOT_DataTemplate_ActionReply(usr_data, buf, sizeof(buf), reply);
+}
+
+// ----------------------------------------------------------------------------
+// Data template upstream
+// ----------------------------------------------------------------------------
+
+static void _cycle_report(void *client)
+{
+ char buf[256];
+ const char *report_property = "{\"power_switch\":0}";
+
+ IotDataTemplateEventData event_data = {
+ .event_id = "status_report",
+ .type = IOT_DATA_TEMPLATE_EVENT_TYPE_INFO,
+ .params = "{\"status\":0,\"message\":\"ok\"}",
+
+ };
+
+ static QcloudIotTimer sg_cycle_report_timer;
+ if (IOT_Timer_Expired(&sg_cycle_report_timer)) {
+ IOT_DataTemplate_PropertyReport(client, buf, sizeof(buf), report_property);
+ IOT_DataTemplate_EventPost(client, buf, sizeof(buf), event_data);
+ IOT_Timer_Countdown(&sg_cycle_report_timer, 500);
+ }
+}
+#endif
+// ----------------------------------------------------------------------------
+// Main
+// ----------------------------------------------------------------------------
+static int sg_main_exit = 0;
+
+#ifdef __linux__
+
+#include
+#include
+#include
+
+static void _main_exit(int sig)
+{
+ Log_e("demo exit by signal:%d\n", sig);
+ sg_main_exit = 1;
+}
+#endif
+
+int main(int argc, char **argv)
+{
+#ifdef __linux__
+ signal(SIGINT, _main_exit);
+#endif
+
+ int rc;
+
+ char buf[1024];
+
+ // init log level
+ LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
+ utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
+
+ DeviceInfo device_info;
+
+ rc = HAL_GetDevInfo((void *)&device_info);
+ if (rc) {
+ Log_e("get device info failed: %d", rc);
+ return rc;
+ }
+
+ // init connection
+ MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
+ _setup_connect_init_params(&init_params, &device_info);
+
+ // create MQTT client and connect with server
+ void *client = IOT_MQTT_Construct(&init_params);
+ if (client) {
+ Log_i("Cloud Device Construct Success");
+ } else {
+ Log_e("MQTT Construct failed!");
+ return QCLOUD_ERR_FAILURE;
+ }
+#if 0
+ // subscribe normal topics and wait result
+ IotDataTemplateCallback callback = {
+ .property_callback = {.method_control_callback = _method_control_callback,
+ .method_clear_control_reply_callback = _method_clear_control_reply_callback,
+ .method_get_status_reply_callback = _method_get_status_reply_callback,
+ .method_report_info_reply_callback = _method_report_info_reply_callback,
+ .method_report_reply_callback = _method_report_reply_callback},
+ .event_callback = {.method_event_reply_callback = _method_event_reply_callback},
+ .action_callback = {.method_action_callback = _method_action_callback},
+ };
+
+ rc = IOT_DataTemplate_Init(client, callback, client);
+ if (rc) {
+ Log_e("Client Subscribe Topic Failed: %d", rc);
+ return rc;
+ }
+
+ const char *report_info =
+ "{\"module_hardinfo\":\"ESP8266\",\"module_softinfo\":\"V1.0\", \"fw_ver\":\"4.0.0\", "
+ "\"imei\":\"11-22-33-44\",\"lat\":\"22.546015\",\"lon\":\"113.941125\",\"mac\":\"11:22:33:44:55:66\",\"device_"
+ "label\":{\"append_"
+ "info\":\"your self defined info\"}}";
+
+ IOT_DataTemplate_PropertyReportInfo(client, buf, sizeof(buf), report_info);
+ IOT_DataTemplate_PropertyGetStatus(client, buf, sizeof(buf));
+#endif
+ // gateway init
+
+ iot_gateway_init(client, 32);
+
+ do {
+ rc = IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT);
+ switch (rc) {
+ case QCLOUD_RET_SUCCESS:
+ break;
+ case QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT:
+ continue;
+ case QCLOUD_RET_MQTT_RECONNECTED:
+ IOT_DataTemplate_PropertyGetStatus(client, buf, sizeof(buf));
+ break;
+ default:
+ Log_e("Exit loop caused of errCode:%d", rc);
+ goto exit;
+ }
+ // _cycle_report(client);
+ } while (!sg_main_exit);
+
+exit:
+ iot_gateway_deinit(client);
+ // IOT_DataTemplate_Deinit(client);
+ rc |= IOT_MQTT_Destroy(&client);
+ utils_log_deinit();
+ return rc;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/sample/gateway_subdev_handle.c b/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/sample/gateway_subdev_handle.c
new file mode 100644
index 00000000..8c1786a2
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/sample/gateway_subdev_handle.c
@@ -0,0 +1,514 @@
+/**
+ * @file gateway_subdev_handle.c
+ * @author {hubert} ({hubertxxu@tencent.com})
+ * @brief
+ * @version 1.0
+ * @date 2022-05-25
+ *
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-05-25 1.0 hubertxxu first commit
+ *
+ */
+
+#include "qcloud_iot_gateway.h"
+#include "utils_list.h"
+
+typedef struct {
+ void *subdev_list;
+ void *mqtt_client;
+} GatewaySubDevHandle;
+
+typedef enum {
+ GATEWAY_SUBDEV_OFFLINE = 0, // 离线
+ GATEWAY_SUBDEV_OFFLINEING = 1, // 发起离线
+ GATEWAY_SUBDEV_ONLINING = 2, // 发起上线
+ GATEWAY_SUBDEV_ONLINE = 3, // 在线
+} GatewaySubDevOnlineStatus;
+
+typedef enum {
+ GATEWAY_SUBDEV_UNBIND = 0,
+ GATEWAY_SUBDEV_UNBINDING = 2,
+ GATEWAY_SUBDEV_BINDING = 3,
+ GATEWAY_SUBDEV_BINDED = 4,
+
+} GatewaySubDevBindStatus;
+
+typedef struct {
+ DeviceInfo dev_info;
+ int online_status;
+ int bind_status;
+ bool is_sub_property;
+ void *mqtt_client;
+} GatewaySubDev;
+
+typedef struct {
+ GatewaySubDev *pair_sub;
+ const char *product_id;
+ const char *device_name;
+} SubDevPair;
+
+typedef enum {
+ DEV_ARRAY_HANDLE_DESCRIBE = 0,
+ DEV_ARRAY_HANDEL_ONLINE = 1,
+ DEV_ARRAY_HANDLE_OFFLINE = 2,
+ DEV_ARRAY_HANDLE_BIND = 3,
+ DEV_ARRAY_HANDLE_UNBIND = 4,
+ DEV_ARRAY_HANDLE_CHANGE_BIND = 5,
+} DevArrayHandleType;
+
+typedef struct {
+ GatewaySubDevHandle *gw_subdev_handle;
+ DevArrayHandleType type;
+} DevArrayHandle;
+
+static GatewaySubDevHandle *sg_gw_subdev_handle = NULL;
+
+GatewaySubDev *iot_gateway_find_sub(GatewaySubDevHandle *sub_handle, const char *sub_pid, const char *sub_dname);
+
+static UtilsListResult _find_subdev_by_name_str(void *list, void *node, void *val, void *usr_data);
+
+// -----------------------------------------------------------------------
+// sub device control && report
+// ------------------------------------------------------------------------
+
+int ext_subdev_control(const char *product_id, const char *device_name, int power_switch, int brightness)
+{
+ Log_d("control device [%s/%s] power_switch : %d, brightness : %d", product_id, device_name, power_switch,
+ brightness);
+ return QCLOUD_RET_SUCCESS;
+}
+
+int ext_subdev_report(const char *product_id, const char *device_name, int power_switch, int brightness)
+{
+ static int token_num = 0;
+ GatewaySubDevHandle *gw_subdev_handle = sg_gw_subdev_handle;
+ POINTER_SANITY_CHECK(gw_subdev_handle, QCLOUD_ERR_FAILURE);
+ POINTER_SANITY_CHECK(product_id, QCLOUD_ERR_FAILURE);
+ POINTER_SANITY_CHECK(device_name, QCLOUD_ERR_FAILURE);
+
+ GatewaySubDev *sub = iot_gateway_find_sub(gw_subdev_handle, product_id, device_name);
+ if (!sub || sub->bind_status != GATEWAY_SUBDEV_BINDED || sub->online_status != GATEWAY_SUBDEV_ONLINE) {
+ if (sub) {
+ Log_e("subdev [%s/%s] illegal, sub->bind_status : %d, sub->online_status : %d", sub->dev_info.product_id,
+ sub->dev_info.device_name, sub->bind_status, sub->online_status);
+ } else {
+ Log_e("subdev [%s/%s] illegal.", product_id, device_name);
+ }
+ return QCLOUD_ERR_FAILURE;
+ }
+
+ char property_topic[MAX_SIZE_OF_CLOUD_TOPIC];
+ char report_buf[256];
+ HAL_Snprintf(property_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$thing/up/property/%s/%s",
+ STRING_PTR_PRINT_SANITY_CHECK(sub->dev_info.product_id),
+ STRING_PTR_PRINT_SANITY_CHECK(sub->dev_info.device_name));
+ int len = HAL_Snprintf(report_buf, sizeof(report_buf),
+ "{\"method\":\"report\",\"clientToken\":\"property-%s-%u\",\"params\":{\"power_switch\":%d, "
+ "\"brightness\":%d}}",
+ device_name, token_num++, power_switch, brightness);
+
+ PublishParams pub_params = DEFAULT_PUB_PARAMS;
+ pub_params.qos = 0;
+ pub_params.payload = (void *)report_buf;
+ pub_params.payload_len = len;
+ return IOT_MQTT_Publish(gw_subdev_handle->mqtt_client, property_topic, &pub_params);
+}
+
+static void _data_template_property_message_handler(void *client, const MQTTMessage *message, void *usr_data)
+{
+ int rc = 0;
+ GatewaySubDev *sub = (GatewaySubDev *)usr_data;
+ char buf[256];
+
+ Log_d("receive subdev [%s/%s] property message:%.*s", sub->dev_info.product_id, sub->dev_info.device_name,
+ message->payload_len, message->payload_str);
+
+ UtilsJsonValue client_token, method;
+ rc = utils_json_value_get("clientToken", strlen("clientToken"), (const char *)message->payload_str,
+ message->payload_len, &client_token);
+ if (rc) {
+ return;
+ }
+ rc = utils_json_value_get("method", strlen("method"), message->payload_str, message->payload_len, &method);
+ if (rc) {
+ return;
+ }
+
+ // only parse control
+ if (strncmp(method.value, "control", method.value_len)) {
+ return;
+ }
+
+ // parse params
+ int power_switch = -1, brightness = -1;
+ utils_json_get_int32("params.power_switch", strlen("params.power_switch"), message->payload_str,
+ message->payload_len, &power_switch);
+ utils_json_get_int32("params.brightness", strlen("params.brightness"), message->payload_str, message->payload_len,
+ &brightness);
+
+ rc = ext_subdev_control(sub->dev_info.product_id, sub->dev_info.device_name, power_switch, brightness);
+
+ char property_topic[MAX_SIZE_OF_CLOUD_TOPIC];
+ HAL_Snprintf(property_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$thing/up/property/%s/%s",
+ STRING_PTR_PRINT_SANITY_CHECK(sub->dev_info.product_id),
+ STRING_PTR_PRINT_SANITY_CHECK(sub->dev_info.device_name));
+ int len = HAL_Snprintf(buf, sizeof(buf), "{\"method\":\"control_reply\",\"clientToken\":\"%.*s\",\"code\":%d}",
+ client_token.value_len, client_token.value, rc);
+
+ PublishParams pub_params = DEFAULT_PUB_PARAMS;
+ pub_params.qos = 0;
+ pub_params.payload = (void *)buf;
+ pub_params.payload_len = len;
+ IOT_MQTT_Publish(client, property_topic, &pub_params);
+ ext_subdev_report(sub->dev_info.product_id, sub->dev_info.device_name, power_switch, brightness);
+}
+
+// ----------------------------------------------------
+// subdev handle
+// -----------------------------------------------------
+
+static UtilsListResult _find_subdev_by_name_str(void *list, void *node, void *val, void *usr_data)
+{
+ GatewaySubDev *sub = (GatewaySubDev *)val;
+ SubDevPair *march_sub = (SubDevPair *)usr_data;
+
+ if (!strncmp(sub->dev_info.product_id, march_sub->product_id, MAX_SIZE_OF_PRODUCT_ID) &&
+ !strncmp(sub->dev_info.device_name, march_sub->device_name, MAX_SIZE_OF_DEVICE_NAME)) {
+ march_sub->pair_sub = sub;
+ return LIST_TRAVERSE_BREAK;
+ }
+ return LIST_TRAVERSE_CONTINUE;
+}
+
+GatewaySubDev *iot_gateway_find_sub(GatewaySubDevHandle *sub_handle, const char *sub_pid, const char *sub_dname)
+{
+ POINTER_SANITY_CHECK(sub_handle, NULL);
+ POINTER_SANITY_CHECK(sub_pid, NULL);
+ POINTER_SANITY_CHECK(sub_dname, NULL);
+
+ SubDevPair sub_info = {NULL, sub_pid, sub_dname};
+
+ utils_list_process(sub_handle->subdev_list, LIST_HEAD, _find_subdev_by_name_str, &sub_info);
+
+ return sub_info.pair_sub;
+}
+
+static UtilsListResult _remove_subdev_by_name_str(void *list, void *node, void *val, void *usr_data)
+{
+ GatewaySubDev *sub = (GatewaySubDev *)val;
+ SubDevPair *march_sub = (SubDevPair *)usr_data;
+
+ if (!strncmp(sub->dev_info.product_id, march_sub->product_id, MAX_SIZE_OF_PRODUCT_ID) &&
+ !strncmp(sub->dev_info.device_name, march_sub->device_name, MAX_SIZE_OF_DEVICE_NAME)) {
+ march_sub->pair_sub = sub;
+ Log_w("remove sub[%s/%s]", sub->dev_info.product_id, sub->dev_info.device_name);
+ // 1. unsub property topic
+ char property_topic[MAX_SIZE_OF_CLOUD_TOPIC];
+ HAL_Snprintf(property_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$thing/down/property/%s/%s",
+ STRING_PTR_PRINT_SANITY_CHECK(sub->dev_info.product_id),
+ STRING_PTR_PRINT_SANITY_CHECK(sub->dev_info.device_name));
+
+ IOT_MQTT_Unsubscribe(sub->mqtt_client, property_topic);
+ // 2. offline?
+
+ // 3. remove
+ utils_list_remove(list, node);
+ return LIST_TRAVERSE_BREAK;
+ }
+ return LIST_TRAVERSE_CONTINUE;
+}
+
+void iot_gateway_remove_subdev_from_sublist(GatewaySubDevHandle *sub_handle, DeviceInfo *sub_dev_info)
+{
+ SubDevPair pair_sub = {
+ .pair_sub = NULL, .device_name = sub_dev_info->device_name, .product_id = sub_dev_info->product_id};
+ utils_list_process(sub_handle->subdev_list, LIST_HEAD, _remove_subdev_by_name_str, &pair_sub);
+}
+
+int iot_gateway_add_sub_to_sublist(GatewaySubDevHandle *sub_handle, DeviceInfo *sub_dev_info, int bind_status)
+{
+ GatewaySubDev *new_sub = iot_gateway_find_sub(sub_handle, sub_dev_info->product_id, sub_dev_info->device_name);
+ if (new_sub) {
+ new_sub->bind_status = bind_status;
+ Log_w("subdev[%s/%s] already exists.", sub_dev_info->product_id, sub_dev_info->device_name);
+ return QCLOUD_RET_SUCCESS;
+ }
+ new_sub = HAL_Malloc(sizeof(GatewaySubDev));
+ POINTER_SANITY_CHECK(new_sub, QCLOUD_ERR_MALLOC);
+ memset(new_sub, 0, sizeof(GatewaySubDev));
+ memcpy(&(new_sub->dev_info), sub_dev_info, sizeof(DeviceInfo));
+ void *node = utils_list_push(sub_handle->subdev_list, new_sub);
+ if (!node) {
+ HAL_Free(new_sub);
+ Log_e("add subdev[%s/%s] error.", sub_dev_info->product_id, sub_dev_info->device_name);
+ return QCLOUD_ERR_FAILURE;
+ }
+ new_sub->bind_status = bind_status;
+ new_sub->mqtt_client = sub_handle->mqtt_client;
+ Log_i("add subdev[%s/%s] in subdev list[total subdev : %d].", sub_dev_info->product_id, sub_dev_info->device_name,
+ utils_list_len_get(sub_handle->subdev_list));
+ return QCLOUD_RET_SUCCESS;
+}
+
+int iot_gateway_bind_subdev(DeviceInfo *sub_dev_info)
+{
+ GatewaySubDevHandle *gw_subdev_handle = sg_gw_subdev_handle;
+ POINTER_SANITY_CHECK(sub_dev_info, QCLOUD_ERR_FAILURE);
+ POINTER_SANITY_CHECK(gw_subdev_handle, QCLOUD_ERR_FAILURE);
+
+ // 1. check if in subdev list
+ GatewaySubDev *sub = iot_gateway_find_sub(gw_subdev_handle, sub_dev_info->product_id, sub_dev_info->device_name);
+ if (sub && sub->bind_status == GATEWAY_SUBDEV_BINDED) {
+ return QCLOUD_RET_SUCCESS;
+ }
+ // 2. add to subdev list
+ iot_gateway_add_sub_to_sublist(gw_subdev_handle, sub_dev_info, GATEWAY_SUBDEV_BINDING);
+
+ // 3. sync cloud
+ char bind_buf[256];
+ const DeviceInfo *bind_dev_list[] = {sub_dev_info};
+ return IOT_Gateway_BindUnbind(gw_subdev_handle->mqtt_client, bind_buf, 256, bind_dev_list, 1, true);
+}
+
+int iot_gateway_subdev_on_or_offline(GatewaySubDevHandle *sub_handle, DeviceInfo *sub_dev_info, bool is_online)
+{
+ char sub_dev_online_buffer[512];
+ const DeviceInfo *dev_online_list[] = {sub_dev_info};
+ // check device in dev list && binded
+ GatewaySubDev *sub_dev = iot_gateway_find_sub(sub_handle, sub_dev_info->product_id, sub_dev_info->device_name);
+
+ if (sub_dev && sub_dev->bind_status == GATEWAY_SUBDEV_BINDED && sub_dev->online_status != GATEWAY_SUBDEV_ONLINE) {
+ sub_dev->online_status = GATEWAY_SUBDEV_ONLINING;
+ return IOT_Gateway_SubOnOffLine(sub_handle->mqtt_client, sub_dev_online_buffer, 512, dev_online_list, 1,
+ is_online);
+ }
+ return QCLOUD_ERR_FAILURE;
+}
+
+int iot_gateway_subdev_subscribe_property(GatewaySubDevHandle *sub_handle, DeviceInfo *sub_dev_info)
+{
+ POINTER_SANITY_CHECK(sub_handle, QCLOUD_ERR_FAILURE);
+ POINTER_SANITY_CHECK(sub_dev_info, QCLOUD_ERR_FAILURE);
+ int rc;
+ // find subdev && check binded && online
+ GatewaySubDev *sub = iot_gateway_find_sub(sub_handle, sub_dev_info->product_id, sub_dev_info->device_name);
+ POINTER_SANITY_CHECK(sub, QCLOUD_ERR_FAILURE);
+ if (sub->bind_status != GATEWAY_SUBDEV_BINDED || sub->online_status != GATEWAY_SUBDEV_ONLINE ||
+ sub->is_sub_property == true) {
+ Log_e("subdev [%s/%s] illegal. bind status : %d online status : %d is_sub_property : %s",
+ sub_dev_info->product_id, sub_dev_info->device_name, sub->bind_status, sub->online_status,
+ sub->is_sub_property ? "true" : "false");
+ return QCLOUD_ERR_FAILURE;
+ }
+
+ char property_topic[MAX_SIZE_OF_CLOUD_TOPIC];
+ HAL_Snprintf(property_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$thing/down/property/%s/%s",
+ STRING_PTR_PRINT_SANITY_CHECK(sub->dev_info.product_id),
+ STRING_PTR_PRINT_SANITY_CHECK(sub->dev_info.device_name));
+ // subscribe normal topics and wait result
+ SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
+ sub_params.on_message_handler = _data_template_property_message_handler;
+ sub_params.qos = QOS1;
+ sub_params.user_data = sub;
+ sub_params.user_data_free = NULL;
+
+ rc = IOT_MQTT_SubscribeSync(sub_handle->mqtt_client, property_topic, &sub_params);
+ if (rc > 0) {
+ sub->is_sub_property = true;
+ }
+ return rc;
+}
+
+int qcloud_gateway_subdev_unsubscribe_property(GatewaySubDevHandle *sub_handle, DeviceInfo *sub_dev_info)
+{
+ POINTER_SANITY_CHECK(sub_handle, QCLOUD_ERR_FAILURE);
+ POINTER_SANITY_CHECK(sub_dev_info, QCLOUD_ERR_FAILURE);
+
+ char property_topic[MAX_SIZE_OF_CLOUD_TOPIC];
+ HAL_Snprintf(property_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$thing/down/property/%s/%s",
+ STRING_PTR_PRINT_SANITY_CHECK(sub_dev_info->product_id),
+ STRING_PTR_PRINT_SANITY_CHECK(sub_dev_info->device_name));
+ return IOT_MQTT_Unsubscribe(sub_handle->mqtt_client, property_topic);
+}
+
+// ----------------------------------------------------
+// gateway callback function
+// -----------------------------------------------------
+
+UtilsJsonArrayIterResult _parse_subdev_array_cb(const char *subdev, int subdev_len, void *usr_data)
+{
+ int rc = 0;
+
+ UtilsJsonValue device_name;
+ UtilsJsonValue product_id;
+ utils_json_value_get("device_name", strlen("device_name"), subdev, subdev_len, &device_name);
+ utils_json_value_get("product_id", strlen("product_id"), subdev, subdev_len, &product_id);
+
+ DeviceInfo dev_info;
+ memset(&dev_info, 0, sizeof(DeviceInfo));
+ strncpy(dev_info.product_id, product_id.value, product_id.value_len);
+ strncpy(dev_info.device_name, device_name.value, device_name.value_len);
+ DevArrayHandle *dev_array_handle = (DevArrayHandle *)usr_data;
+
+ switch (dev_array_handle->type) {
+ case DEV_ARRAY_HANDLE_CHANGE_BIND:
+ case DEV_ARRAY_HANDLE_DESCRIBE:
+ // subdev has be bind in cloud. add this to subdev list
+ rc = iot_gateway_add_sub_to_sublist(dev_array_handle->gw_subdev_handle, &dev_info, GATEWAY_SUBDEV_BINDED);
+ if (!rc) {
+ iot_gateway_subdev_on_or_offline(dev_array_handle->gw_subdev_handle, &dev_info, true);
+ }
+ break;
+ case DEV_ARRAY_HANDEL_ONLINE: {
+ int result = -1;
+ GatewaySubDev *sub_dev =
+ iot_gateway_find_sub(dev_array_handle->gw_subdev_handle, dev_info.product_id, dev_info.device_name);
+ utils_json_get_int32("result", strlen("result"), subdev, subdev_len, &result);
+ if (sub_dev && 0 == result) {
+ sub_dev->online_status = GATEWAY_SUBDEV_ONLINE;
+ iot_gateway_subdev_subscribe_property(dev_array_handle->gw_subdev_handle, &dev_info);
+ } else if (sub_dev) {
+ sub_dev->online_status = result;
+ }
+ } break;
+ case DEV_ARRAY_HANDLE_BIND: {
+ int result = -1;
+ GatewaySubDev *sub_dev =
+ iot_gateway_find_sub(dev_array_handle->gw_subdev_handle, dev_info.product_id, dev_info.device_name);
+ if (!sub_dev) {
+ break;
+ }
+
+ utils_json_get_int32("result", strlen("result"), subdev, subdev_len, &result);
+ if (IOT_GATEWAY_RET_SUCCESS == result || IOT_GATEWAY_ERR_BIND_REPEAT == result) {
+ sub_dev->bind_status = GATEWAY_SUBDEV_BINDED;
+ iot_gateway_subdev_on_or_offline(dev_array_handle->gw_subdev_handle, &dev_info, true);
+ } else {
+ sub_dev->bind_status = result;
+ }
+ } break;
+ case DEV_ARRAY_HANDLE_UNBIND: {
+ iot_gateway_remove_subdev_from_sublist(dev_array_handle->gw_subdev_handle, &dev_info);
+ } break;
+
+ default:
+ break;
+ }
+
+ return UTILS_JSON_ARRAY_ITER_CONTINUE;
+}
+
+static void _gateway_bind_unbind_reply_callback(UtilsJsonValue sub_devices, bool is_bind, void *usr_data)
+{
+ GatewaySubDevHandle *gw_subdev_handle = (GatewaySubDevHandle *)usr_data;
+ Log_d("bind status : %d %.*s", is_bind, sub_devices.value_len, sub_devices.value);
+ DevArrayHandle dev_array_handle = {.gw_subdev_handle = gw_subdev_handle,
+ .type = is_bind ? DEV_ARRAY_HANDLE_BIND : DEV_ARRAY_HANDLE_UNBIND};
+ utils_json_array_parse(sub_devices.value, sub_devices.value_len, _parse_subdev_array_cb, &dev_array_handle);
+}
+
+static void _gateway_unbind_all_callback(void *usr_data)
+{
+ // GatewaySubDevHandle *gw_subdev_handle = (GatewaySubDevHandle *)usr_data;
+ Log_d("unbind all.");
+}
+
+static void _gateway_online_offline_reply_callback(UtilsJsonValue sub_devices, bool is_online, void *usr_data)
+{
+ GatewaySubDevHandle *gw_subdev_handle = (GatewaySubDevHandle *)usr_data;
+ Log_d("online status : %d %.*s", is_online, sub_devices.value_len, sub_devices.value);
+ DevArrayHandle dev_array_handle = {.gw_subdev_handle = gw_subdev_handle,
+ .type = is_online ? DEV_ARRAY_HANDEL_ONLINE : DEV_ARRAY_HANDLE_OFFLINE};
+ utils_json_array_parse(sub_devices.value, sub_devices.value_len, _parse_subdev_array_cb, &dev_array_handle);
+}
+
+static void _gateway_search_device_callback(bool is_on, void *usr_data)
+{
+ // GatewaySubDevHandle *gw_subdev_handle = (GatewaySubDevHandle *)usr_data;
+ Log_d("search : %d", is_on);
+}
+
+static void _gateway_describe_subdevices_reply_callback(UtilsJsonValue sub_devices, void *usr_data)
+{
+ GatewaySubDevHandle *gw_subdev_handle = (GatewaySubDevHandle *)usr_data;
+ DevArrayHandle dev_array_handle = {.gw_subdev_handle = gw_subdev_handle, .type = DEV_ARRAY_HANDLE_DESCRIBE};
+ Log_d("describe %.*s", sub_devices.value_len, sub_devices.value);
+ utils_json_array_parse(sub_devices.value, sub_devices.value_len, _parse_subdev_array_cb, &dev_array_handle);
+}
+
+static void _gateway_change_subdevices_status_callback(UtilsJsonValue sub_devices, bool is_bind, void *usr_data)
+{
+ GatewaySubDevHandle *gw_subdev_handle = (GatewaySubDevHandle *)usr_data;
+ Log_d("change sub devices is bind %d %.*s", is_bind, sub_devices.value_len, sub_devices.value);
+ DevArrayHandle dev_array_handle = {.gw_subdev_handle = gw_subdev_handle,
+ .type = is_bind ? DEV_ARRAY_HANDLE_CHANGE_BIND : DEV_ARRAY_HANDLE_UNBIND};
+ utils_json_array_parse(sub_devices.value, sub_devices.value_len, _parse_subdev_array_cb, &dev_array_handle);
+}
+
+// -----------------------------------------------------------------
+// export api
+// -----------------------------------------------------------------
+
+int iot_gateway_init(void *client, int sub_dev_max)
+{
+ int rc = 0;
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_FAILURE);
+ // 1. init gateway subdev handle
+ GatewaySubDevHandle *gw_subdev_handle = HAL_Malloc(sizeof(GatewaySubDevHandle));
+ POINTER_SANITY_CHECK(gw_subdev_handle, QCLOUD_ERR_MALLOC);
+ UtilsListFunc func = DEFAULT_LIST_FUNCS;
+ gw_subdev_handle->subdev_list = utils_list_create(func, sub_dev_max);
+ if (!gw_subdev_handle->subdev_list) {
+ goto _exit;
+ }
+ gw_subdev_handle->mqtt_client = client;
+ sg_gw_subdev_handle = gw_subdev_handle;
+ // 2. sub gateway topic
+ IotGatewayMessageCallback callback = {
+ .bind_unbind_reply_callback = _gateway_bind_unbind_reply_callback,
+ .change_subdevices_status_callback = _gateway_change_subdevices_status_callback,
+ .describe_subdevices_reply_callback = _gateway_describe_subdevices_reply_callback,
+ .online_offline_reply_callback = _gateway_online_offline_reply_callback,
+ .search_device_callback = _gateway_search_device_callback,
+ .unbind_all_callback = _gateway_unbind_all_callback,
+ };
+ rc = IOT_Gateway_Init(client, callback, gw_subdev_handle);
+ if (rc < 0) {
+ goto _exit;
+ }
+
+ // 3. sync subdev list from cloud
+ return IOT_Gateway_Describe(client);
+
+_exit:
+ utils_list_destroy(gw_subdev_handle->subdev_list);
+ HAL_Free(gw_subdev_handle);
+ return QCLOUD_ERR_FAILURE;
+}
+
+int iot_gateway_deinit(void *client)
+{
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_FAILURE);
+ GatewaySubDevHandle *gw_subdev_handle = IOT_Gateway_GetUsrData(client);
+ IOT_Gateway_Deinit(client);
+ POINTER_SANITY_CHECK(gw_subdev_handle, QCLOUD_ERR_FAILURE);
+ utils_list_destroy(gw_subdev_handle->subdev_list);
+ HAL_Free(gw_subdev_handle);
+ return QCLOUD_RET_SUCCESS;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/sample/gateway_subdev_handle.h b/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/sample/gateway_subdev_handle.h
new file mode 100644
index 00000000..0d49108c
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/sample/gateway_subdev_handle.h
@@ -0,0 +1,44 @@
+/**
+ * @file gateway_subdev_handle.h
+ * @author {hubert} ({hubertxxu@tencent.com})
+ * @brief
+ * @version 1.0
+ * @date 2022-05-25
+ *
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-05-25 1.0 hubertxxu first commit
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_GATEWAY_SAMPLE_GATEWAY_SUBDEV_HANDLE_H_
+#define IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_GATEWAY_SAMPLE_GATEWAY_SUBDEV_HANDLE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int iot_gateway_init(void *client, int sub_dev_max);
+
+int iot_gateway_deinit(void *client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_GATEWAY_SAMPLE_GATEWAY_SUBDEV_HANDLE_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/src/gateway_mqtt.c b/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/src/gateway_mqtt.c
new file mode 100644
index 00000000..bad00769
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/src/gateway_mqtt.c
@@ -0,0 +1,470 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file gateway_mqtt.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2022-05-10
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-05-10 1.0 fancyxu first commit
+ *
+ */
+
+#include "qcloud_iot_gateway.h"
+
+#include "utils_hmac.h"
+#include "utils_base64.h"
+
+// ----------------------------------------------------------------------------
+// downstream message
+// ----------------------------------------------------------------------------
+
+/**
+ * @brief Context of gateway callback and user data.
+ *
+ */
+typedef struct {
+ IotGatewayMessageCallback callback;
+ void *usr_data;
+} GatewayContext;
+
+/**
+ * @brief Gateway message type.
+ *
+ */
+typedef enum {
+ TYPE_BIND = 0,
+ TYPE_UNBIND,
+ TYPE_UNBIND_ALL,
+ TYPE_ONLINE,
+ TYPE_OFFLINE,
+ TYPE_SEARCH_DEVICES,
+ TYPE_DESCRIBE_SUB_DEVICES,
+ TYPE_CHANGE,
+} GatewayMessageType;
+
+/**
+ * @brief Get message type.
+ *
+ * @param[in] json_buf
+ * @param[in] buf_len
+ * @return < 0 for fail, others @see GatewayMessageType
+ */
+static int _parse_gateway_message_type(const char *json_buf, int buf_len)
+{
+ UtilsJsonValue type;
+ // parse type
+ int rc = utils_json_value_get("type", sizeof("type") - 1, json_buf, buf_len, &type);
+ if (rc) {
+ Log_e("parse gateway msg type error");
+ return rc;
+ }
+
+ /**
+ * @brief Order @see GatewayMessageType
+ *
+ */
+ const char *gateway_type_str[] = {
+ [TYPE_BIND] = "bind",
+ [TYPE_UNBIND] = "unbind",
+ [TYPE_UNBIND_ALL] = "unbind_all",
+ [TYPE_ONLINE] = "online",
+ [TYPE_OFFLINE] = "offline",
+ [TYPE_SEARCH_DEVICES] = "search_devices",
+ [TYPE_DESCRIBE_SUB_DEVICES] = "describe_sub_devices",
+ [TYPE_CHANGE] = "change",
+ };
+
+ for (int i = 0; i < sizeof(gateway_type_str) / sizeof(const char *); i++) {
+ if (!strncmp(type.value, gateway_type_str[i], type.value_len)) {
+ return i;
+ }
+ }
+ return QCLOUD_ERR_FAILURE;
+}
+
+static void _callback_gateway(GatewayMessageType msg_type, IotGatewayMessageCallback *callback,
+ UtilsJsonValue sub_devices, int status, void *usr_data)
+{
+ switch (msg_type) {
+ case TYPE_BIND:
+ case TYPE_UNBIND:
+ if (callback->bind_unbind_reply_callback) {
+ callback->bind_unbind_reply_callback(sub_devices, msg_type == TYPE_BIND, usr_data);
+ }
+ break;
+ case TYPE_UNBIND_ALL:
+ if (callback->unbind_all_callback) {
+ callback->unbind_all_callback(usr_data);
+ }
+ break;
+ case TYPE_ONLINE:
+ case TYPE_OFFLINE:
+ if (callback->online_offline_reply_callback) {
+ callback->online_offline_reply_callback(sub_devices, msg_type == TYPE_ONLINE, usr_data);
+ }
+ break;
+ case TYPE_SEARCH_DEVICES:
+ if (callback->search_device_callback) {
+ callback->search_device_callback(status, usr_data);
+ }
+ break;
+ case TYPE_DESCRIBE_SUB_DEVICES:
+ if (callback->describe_subdevices_reply_callback) {
+ callback->describe_subdevices_reply_callback(sub_devices, usr_data);
+ }
+ break;
+ case TYPE_CHANGE:
+ // status : 0:unbind 1:bind
+ if (callback->change_subdevices_status_callback) {
+ callback->change_subdevices_status_callback(sub_devices, status, usr_data);
+ }
+ break;
+ default:
+ Log_e("unsupported now");
+ break;
+ }
+}
+
+/**
+ * @brief Gateway downstream messsage handle.
+ *
+ * @param[in] client pointer to mqtt client
+ * @param[in] message @see MQTTMessage
+ * @param[in] user_data
+ */
+static void _gateway_mqtt_message_handler(void *client, const MQTTMessage *message, void *user_data)
+{
+ int rc = 0;
+ Log_d("receive gateway message %.*s", message->payload_len, message->payload_str);
+
+ int msg_type = _parse_gateway_message_type(message->payload_str, message->payload_len);
+ if (msg_type < 0) {
+ return;
+ }
+ GatewayContext *gateway_context = user_data;
+ Log_i("get type %d from server", msg_type);
+
+ UtilsJsonValue sub_devices;
+ int32_t status;
+
+ if (msg_type != TYPE_SEARCH_DEVICES && msg_type != TYPE_UNBIND_ALL) {
+ rc = utils_json_value_get("payload.devices", sizeof("payload.devices") - 1, message->payload_str,
+ message->payload_len, &sub_devices);
+ if (rc) {
+ Log_e("invalid gateway payload.");
+ return;
+ }
+ }
+
+ if (msg_type == TYPE_SEARCH_DEVICES || msg_type == TYPE_CHANGE) {
+ rc = utils_json_get_int32("payload.status", sizeof("payload.status") - 1, message->payload_str,
+ message->payload_len, &status);
+ if (rc) {
+ return;
+ }
+ }
+
+ _callback_gateway(msg_type, &gateway_context->callback, sub_devices, status, gateway_context->usr_data);
+}
+
+/**
+ * @brief Subscribe gateway result topic.
+ *
+ * @param[in] client pointer to mqtt client
+ * @param[in] callback @see IotGatewayMessageCallback
+ * @param[in] usr_data
+ * @return @see IotReturnCode
+ */
+static int _gateway_mqtt_subscribe(void *client, IotGatewayMessageCallback callback, void *usr_data)
+{
+ GatewayContext *gateway_context = HAL_Malloc(sizeof(GatewayContext));
+ if (!gateway_context) {
+ return QCLOUD_ERR_MALLOC;
+ }
+ gateway_context->callback = callback;
+ gateway_context->usr_data = usr_data;
+
+ char gateway_topic[MAX_SIZE_OF_CLOUD_TOPIC];
+ HAL_Snprintf(gateway_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$gateway/operation/result/%s/%s",
+ STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
+ STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
+
+ SubscribeParams sub_param = DEFAULT_SUB_PARAMS;
+ sub_param.on_message_handler = _gateway_mqtt_message_handler;
+ sub_param.user_data = gateway_context;
+ sub_param.user_data_free = HAL_Free;
+
+ int rc = IOT_MQTT_SubscribeSync(client, gateway_topic, &sub_param);
+ if (rc) {
+ HAL_Free(gateway_context);
+ }
+ return rc;
+}
+
+// ----------------------------------------------------------------------------
+// server mqtt context
+// ----------------------------------------------------------------------------
+
+/**
+ * @brief Calculate bind signature
+ *
+ * @param[in] subdev_info subdev info
+ * @param[in] nonce random
+ * @param[in] timestamp timestamp
+ * @param[out] signature signature
+ * @return 0 for success
+ */
+static int _subdev_bind_hmac_sha1_cal(const DeviceInfo *subdev_info, int nonce, uint64_t timestamp, char *signature)
+{
+#define MAX_SIGN_BUF_SIZE (MAX_SIZE_OF_CLIENT_ID + 48)
+ size_t olen = 0;
+
+ char sign_buf[MAX_SIGN_BUF_SIZE] = {0};
+ int sign_buf_len;
+ char sign[SHA1_DIGEST_SIZE] = {0};
+
+ /* 1. sign fmt : ${product_id}${device_name};${random};${expiration_time} */
+ sign_buf_len = HAL_Snprintf(sign_buf, sizeof(sign_buf), "%s%s;%d;%llu", subdev_info->product_id,
+ subdev_info->device_name, nonce, timestamp);
+ /* 2. cal hmac sha1 */
+ utils_hmac_sha1(sign_buf, sign_buf_len, (const uint8_t *)subdev_info->device_secret,
+ strlen(subdev_info->device_secret), sign);
+ /* 3. base64 encode */
+ return utils_base64encode((uint8_t *)signature, 31, &olen, (const uint8_t *)sign, SHA1_DIGEST_SIZE);
+}
+
+/**
+ * @brief Publish gateway message.
+ *
+ * @param[in] client pointer to mqtt client
+ * @param[in] qos @see QoS
+ * @param[in] payload message payload
+ * @param[in] payload_len payload length
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+static int _gateway_mqtt_publish(void *client, QoS qos, const char *payload, int payload_len)
+{
+ char gateway_topic[MAX_SIZE_OF_CLOUD_TOPIC];
+ HAL_Snprintf(gateway_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$gateway/operation/%s/%s",
+ STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
+ STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
+
+ PublishParams pub_params = DEFAULT_PUB_PARAMS;
+ pub_params.qos = qos;
+ pub_params.payload = (void *)payload;
+ pub_params.payload_len = payload_len;
+ return IOT_MQTT_Publish(client, gateway_topic, &pub_params);
+}
+
+// ----------------------------------------------------------------------------
+// API
+// ----------------------------------------------------------------------------
+
+/**
+ * @brief Subscribe gateway topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] callback @see IotGatewayMessageCallback
+ * @param[in] usr_data usr data using in callback
+ * @return @see IotReturnCode
+ */
+int IOT_Gateway_Init(void *client, IotGatewayMessageCallback callback, void *usr_data)
+{
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
+ return _gateway_mqtt_subscribe(client, callback, usr_data);
+}
+
+/**
+ * @brief Unsubscribe gateway topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ */
+void IOT_Gateway_Deinit(void *client)
+{
+ POINTER_SANITY_CHECK_RTN(client);
+
+ char gateway_topic[MAX_SIZE_OF_CLOUD_TOPIC];
+ HAL_Snprintf(gateway_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$gateway/operation/result/%s/%s",
+ STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
+ STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
+ IOT_MQTT_Unsubscribe(client, gateway_topic);
+}
+
+/**
+ * @brief Get gateway init usr data.
+ *
+ * @param [in,out] client pointer to mqtt client
+ * @return usr data or NULL
+ */
+void *IOT_Gateway_GetUsrData(void *client)
+{
+ POINTER_SANITY_CHECK(client, NULL);
+ char gateway_topic[MAX_SIZE_OF_CLOUD_TOPIC];
+ HAL_Snprintf(gateway_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$gateway/operation/result/%s/%s",
+ STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
+ STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
+ GatewayContext *gateway_context = IOT_MQTT_GetSubUsrData(client, gateway_topic);
+ if (gateway_context && gateway_context->usr_data) {
+ return gateway_context->usr_data;
+ }
+ return NULL;
+}
+
+/**
+ * @brief Publish subdevice online/offline message.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] buf buffer to hold publish message
+ * @param[in] buf_len buffer length
+ * @param[in] sub_dev_list subdevice list
+ * @param[in] num num of subdevice, @note only one subdevice is supported now.
+ * @param[in] is_online 1: online; 0: offline
+ * @return @see packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_Gateway_SubOnOffLine(void *client, char *buf, int buf_len, const DeviceInfo *sub_dev_list[], int num,
+ bool is_online)
+{
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
+ POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
+ POINTER_SANITY_CHECK(sub_dev_list, QCLOUD_ERR_INVAL);
+ POINTER_SANITY_CHECK(*sub_dev_list, QCLOUD_ERR_INVAL);
+ NUMBERIC_SANITY_CHECK(num, QCLOUD_ERR_INVAL);
+ if (num > 1) {
+ Log_e("Only one subdevice is supported!");
+ return QCLOUD_ERR_INVAL;
+ }
+
+ int len;
+ len = HAL_Snprintf(buf, buf_len, "{\"type\":\"%s\",\"payload\":{\"devices\":[", is_online ? "online" : "offline");
+ for (int i = 0; i < num; i++) {
+ len += HAL_Snprintf(buf + len, buf_len - len, "{\"product_id\":\"%s\",\"device_name\":\"%s\"},",
+ sub_dev_list[i]->product_id, sub_dev_list[i]->device_name);
+ }
+ len += HAL_Snprintf(buf + len - 1, buf_len - len + 1, "]}}"); // -1 remove ','
+ return _gateway_mqtt_publish(client, QOS0, buf, len);
+}
+
+/**
+ * @brief Publish subdevice bind/unbind message.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] buf buffer to hold publish message
+ * @param[in] buf_len buffer length
+ * @param[in] sub_dev_list subdevice list
+ * @param[in] num num of subdevice, @note only one subdevice is supported now.
+ * @param[in] is_bind 1: bind; 0: unbind
+ * @return @see packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_Gateway_BindUnbind(void *client, char *buf, int buf_len, const DeviceInfo *sub_dev_list[], int num,
+ bool is_bind)
+{
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
+ POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
+ POINTER_SANITY_CHECK(sub_dev_list, QCLOUD_ERR_INVAL);
+ NUMBERIC_SANITY_CHECK(num, QCLOUD_ERR_INVAL);
+
+ if (num > 1) {
+ Log_e("Only one subdevice is supported!");
+ return QCLOUD_ERR_INVAL;
+ }
+
+ srand(IOT_Timer_CurrentSec());
+ int nonce = rand();
+ uint64_t timestamp = IOT_Timer_CurrentSec();
+
+ char signature[31] = {0};
+ int rc, len;
+ len = HAL_Snprintf(buf, buf_len, "{\"type\":\"%s\",\"payload\":{\"devices\":[", is_bind ? "bind" : "unbind");
+ for (int i = 0; i < num; i++) {
+ if (is_bind) {
+ rc = _subdev_bind_hmac_sha1_cal(sub_dev_list[i] + i, nonce, timestamp, signature);
+ if (rc) {
+ Log_e("calculate signature fail");
+ return QCLOUD_ERR_FAILURE;
+ }
+ len += HAL_Snprintf(buf + len, buf_len - len,
+ "{\"product_id\":\"%s\",\"device_name\":\"%s\",\"signature\":\"%s\",\"random\":%d,"
+ "\"timestamp\":%lld,\"signmethod\":\"hmacsha1\",\"authtype\":\"psk\"},",
+ sub_dev_list[i]->product_id, sub_dev_list[i]->device_name, signature, nonce, timestamp);
+ } else {
+ len += HAL_Snprintf(buf + len, buf_len - len, "{\"product_id\":\"%s\",\"device_name\":\"%s\"},",
+ sub_dev_list[i]->product_id, sub_dev_list[i]->device_name);
+ }
+ }
+ len += HAL_Snprintf(buf + len - 1, buf_len - len + 1, "]}}"); // -1 remove ','
+ return _gateway_mqtt_publish(client, QOS0, buf, len);
+}
+
+/**
+ * @brief Publish subdevice describe message.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return @see packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_Gateway_Describe(void *client)
+{
+#define GATEWAY_UP_DESCRIBE "{\"type\":\"describe_sub_devices\"}"
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
+ return _gateway_mqtt_publish(client, QOS0, GATEWAY_UP_DESCRIBE, sizeof(GATEWAY_UP_DESCRIBE) - 1);
+}
+
+/**
+ * @brief Publish search device reply message.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] is_on 1: on; 0: off
+ * @param[in] result 0: success; 1: fail
+ * @return @see packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_Gateway_SearchDeviceReply(void *client, bool is_on, int result)
+{
+ char buf[64];
+ int len = HAL_Snprintf(buf, sizeof(buf), "{\"type\":\"search_devices\",\"payload\":{\"status\":%d,\"result\":%d}}",
+ is_on, result);
+ return _gateway_mqtt_publish(client, QOS0, buf, len);
+}
+
+/**
+ * @brief Publish unbind all reply message.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] result 0: success; 1: fail
+ * @return @see packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+int IOT_Gateway_UnbindAllReply(void *client, int result)
+{
+ char buf[64];
+ int len = HAL_Snprintf(buf, sizeof(buf), "{\"type\":\"unbind_all\",\"payload\":{\"result\":%d}}", result);
+ return _gateway_mqtt_publish(client, QOS0, buf, len);
+}
+
+#if 0 // TODO: no need now for useless
+int IOT_Gateway_ChangeReply(void *client, char *buf, int buf_len, DeviceInfo *sub_dev_list, int num, int status)
+{
+ char buf[64];
+ int len = HAL_Snprintf(buf, sizeof(buf), "{\"type\":\"change\",\"payload\":{\"status\":%d,\"devices\":[", status);
+ for (int i = 0; i < num; i++) {
+ len += HAL_Snprintf(buf + len, buf_len - len, "{\"product_id\":\"%s\",\"device_name\":\"%s\"},",
+ sub_dev_list[i].product_id, sub_dev_list[i].device_name);
+ }
+ len += HAL_Snprintf(buf + len - 1, buf_len - len + 1, "]}}"); // -1 remove ','
+ return _gateway_mqtt_publish(client, QOS0, buf, len);
+}
+#endif
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/test/.clang-format b/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/test/.clang-format
new file mode 100644
index 00000000..334c1c0d
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/test/.clang-format
@@ -0,0 +1,8 @@
+Language: Cpp
+BasedOnStyle: Google
+ColumnLimit: 120
+DerivePointerAlignment: true
+PointerAlignment: Left
+SortIncludes: true
+IncludeBlocks: Preserve
+IndentPPDirectives: AfterHash
\ No newline at end of file
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/test/test_gateway.cc b/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/test/test_gateway.cc
new file mode 100644
index 00000000..59b35fb5
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/gateway/test/test_gateway.cc
@@ -0,0 +1,88 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file test_data_template.cc
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-09-29
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-09-29 1.0 fancyxu first commit
+ *
+ */
+
+#include
+#include
+
+#include "gtest/gtest.h"
+#include "mqtt_client_test.h"
+#include "qcloud_iot_common.h"
+#include "qcloud_iot_explorer.h"
+
+namespace mqtt_client_unittest {
+
+static void _bind_unbind_reply_callback(UtilsJsonValue sub_devices, bool is_bind, void *usr_data) { Log_d("recv msg"); }
+static void _unbind_all_callback(void *usr_data) { Log_d("recv msg"); }
+static void _online_offline_reply_callback(UtilsJsonValue sub_devices, bool is_online, void *usr_data) {
+ Log_d("recv msg");
+}
+static void _search_device_callback(bool is_on, void *usr_data) { Log_d("recv msg"); }
+static void _describe_subdevices_reply_callback(UtilsJsonValue sub_devices, void *usr_data) { Log_d("recv msg"); }
+static void _change_subdevices_status_callback(UtilsJsonValue sub_devices, bool is_bind, void *usr_data) {
+ Log_d("recv msg");
+}
+#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2 * !!(condition)]))
+/**
+ * @brief Test data template action.
+ *
+ */
+TEST_F(MqttClientTest, gateway) {
+ char buf[1024];
+ IotGatewayMessageCallback callback = {
+ .bind_unbind_reply_callback = _bind_unbind_reply_callback,
+ .unbind_all_callback = _unbind_all_callback,
+ .online_offline_reply_callback = _online_offline_reply_callback,
+ .search_device_callback = _search_device_callback,
+ .describe_subdevices_reply_callback = _describe_subdevices_reply_callback,
+ .change_subdevices_status_callback = _change_subdevices_status_callback,
+ };
+
+ DeviceInfo sub_dev0;
+ HAL_GetDevInfo(&sub_dev0);
+
+ const DeviceInfo *subdev[1] = {0};
+ subdev[0] = &sub_dev0;
+
+ ASSERT_EQ(IOT_Gateway_Init(client, callback, client), 0);
+ ASSERT_GE(IOT_Gateway_BindUnbind(client, buf, sizeof(buf), subdev, sizeof(subdev) / sizeof(DeviceInfo *), true), 0);
+ IOT_MQTT_Yield(client, 100);
+ ASSERT_GE(IOT_Gateway_Describe(client), 0);
+ IOT_MQTT_Yield(client, 100);
+ ASSERT_GE(IOT_Gateway_SubOnOffLine(client, buf, sizeof(buf), subdev, sizeof(subdev) / sizeof(DeviceInfo *), 1), 0);
+ IOT_MQTT_Yield(client, 100);
+ ASSERT_GE(IOT_Gateway_SubOnOffLine(client, buf, sizeof(buf), subdev, sizeof(subdev) / sizeof(DeviceInfo *), 0), 0);
+ IOT_MQTT_Yield(client, 100);
+ ASSERT_GE(IOT_Gateway_BindUnbind(client, buf, sizeof(buf), subdev, sizeof(subdev) / sizeof(DeviceInfo *), false), 0);
+ IOT_MQTT_Yield(client, 100);
+
+ ASSERT_GE(IOT_Gateway_SearchDeviceReply(client, true, 0), 0);
+ ASSERT_GE(IOT_Gateway_UnbindAllReply(client, 0), 0);
+ IOT_Gateway_Deinit(client);
+}
+
+} // namespace mqtt_client_unittest
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/http_client/CMakeLists.txt b/components/connectivity/iot-hub-device-c-sdk/services/common/http_client/CMakeLists.txt
new file mode 100644
index 00000000..cb0c50e3
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/http_client/CMakeLists.txt
@@ -0,0 +1,2 @@
+file(GLOB src_http_client ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
+set(src_services ${src_services} ${src_http_client} PARENT_SCOPE)
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/http_client/src/http_client.c b/components/connectivity/iot-hub-device-c-sdk/services/common/http_client/src/http_client.c
new file mode 100644
index 00000000..db10cbcd
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/http_client/src/http_client.c
@@ -0,0 +1,629 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file http_client.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-10-22
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-10-22 1.0 fancyxu first commit
+ *
+ */
+
+#include "qcloud_iot_http_client.h"
+
+#include "network_interface.h"
+
+/**
+ * @brief Http data.
+ *
+ */
+typedef struct {
+ const char *data;
+ int data_len;
+} HTTPData;
+
+/**
+ * @brief Only focus on response code and content,
+ * @ref https://datatracker.ietf.org/doc/html/rfc7231
+ *
+ */
+typedef struct {
+ int status_code; /**< https://en.wikipedia.org/wiki/List_of_HTTP_status_codes */
+ int content_length; /**< content length */
+ uint8_t *content_buf; /**< content buffer */
+ int content_buf_len; /**< content buffer length */
+ int recv_len; /**< already recv length */
+ int need_recv_len; /**< need recv length */
+ int is_chunked; /**< support chunked */
+} IotHTTPResponseData;
+
+/**
+ * @brief HTTP client.
+ *
+ */
+typedef struct {
+ IotHTTPResponseData response;
+ IotNetwork network;
+} IotHTTPClient;
+
+/**************************************************************************************
+ * network
+ **************************************************************************************/
+
+/**
+ * @brief Connect with host.
+ *
+ * @param[in,out] client pointer to http client. @see IotHTTPClient
+ * @param[in] host http host
+ * @param[in] port http port, 80(http)/443(https)
+ * @param[in] ca_crt https ca
+ * @return 0 for success. others @see IotReturnCode
+ */
+static int _http_client_connect(IotHTTPClient *client, const char *host, const char *port, const char *ca_crt)
+{
+ int rc = 0;
+
+ client->network.type = IOT_NETWORK_TYPE_TCP;
+#if !defined(AUTH_WITH_NO_TLS) && defined(AUTH_MODE_CERT)
+ client->network.ssl_connect_params.ca_crt = ca_crt;
+ client->network.ssl_connect_params.ca_crt_len = strlen(ca_crt);
+ client->network.ssl_connect_params.cert_file = NULL;
+ client->network.ssl_connect_params.key_file = NULL;
+ client->network.ssl_connect_params.timeout_ms = HTTPS_READ_TIMEOUT_MS;
+#endif
+ client->network.host = host;
+ client->network.port = port;
+
+ rc = qcloud_iot_network_init(&(client->network));
+ return rc ? rc : client->network.connect(&(client->network));
+}
+
+/**
+ * @brief Disconnect with host.
+ *
+ * @param[in,out] client pointer to http client. @see IotHTTPClient
+ */
+static void _http_client_disconnect(IotHTTPClient *client)
+{
+ return client->network.disconnect(&(client->network));
+}
+
+/**
+ * @brief Send data to http server.
+ *
+ * @param[in,out] client pointer to http client. @see IotHTTPClient
+ * @param[in] buf buffer tot send
+ * @param[in] len buffer length
+ * @return 0 for success. others @see IotReturnCode
+ */
+static int _http_client_send(IotHTTPClient *client, char *buf, uint32_t len)
+{
+ size_t written_len;
+ return client->network.write(&(client->network), (uint8_t *)buf, len, HTTP_WRITE_TIMEOUT_MS, &written_len);
+}
+
+/**
+ * @brief Recv data from http server.
+ *
+ * @param[in,out] client pointer to http client. @see IotHTTPClient
+ * @param[out] buf
+ * @param[in] len
+ * @param[in] timeout_ms
+ * @return >0 for read data length, others @see IotReturnCode
+ */
+static int _http_client_recv(IotHTTPClient *client, uint8_t *buf, uint32_t len, uint32_t timeout_ms)
+{
+ size_t read_len;
+
+ int rc = client->network.read(&client->network, buf, len, timeout_ms, &read_len);
+ switch (rc) {
+ case QCLOUD_ERR_TCP_NOTHING_TO_READ:
+ return QCLOUD_RET_SUCCESS;
+ default:
+ return rc ? rc : read_len;
+ }
+}
+
+/**************************************************************************************
+ * request
+ **************************************************************************************/
+
+/**
+ * @brief Parse url and get host & path.
+ *
+ * @param[in] url url str
+ * @param[out] host pointer to host str and length. @see HTTPData
+ * @param[out] path pointer to path str and length. @see HTTPData
+ * @return 0 for success. others @see IotReturnCode
+ */
+static int _http_client_parse_url(const char *url, HTTPData *host, HTTPData *path)
+{
+ // start from: http:// or https://
+ char *host_ptr = (char *)strstr(url, "://");
+ if (!host_ptr) {
+ return QCLOUD_ERR_HTTP_PARSE;
+ }
+ host->data = host_ptr + 3;
+
+ path->data = strchr(host->data, '/');
+ if (!path->data) {
+ host->data_len = url + strlen(url) - host->data;
+ path->data_len = 1;
+ path->data = "/";
+ return QCLOUD_RET_SUCCESS;
+ }
+
+ host->data_len = path->data - host->data;
+
+ char *fragment_ptr = strchr(host_ptr, '#');
+ path->data_len = fragment_ptr ? fragment_ptr - path->data : strlen(path->data);
+ return QCLOUD_RET_SUCCESS;
+}
+
+/**
+ * @brief Send http request line.
+ *
+ * @param[in,out] client pointer to http client. @see IotHTTPClient
+ * @param[in] params params needed to send request to http server, @see IotHTTPRequestParams
+ * @return int
+ */
+static int _http_client_send_request_line(IotHTTPClient *client, const IotHTTPRequestParams *params)
+{
+ int len, rc, buf_len;
+ char *buf;
+ HTTPData host, path;
+
+ /**
+ * @brief order @see IotHTTPMethod
+ *
+ */
+ const char *method_str[] = {
+ [IOT_HTTP_METHOD_GET] = "GET", [IOT_HTTP_METHOD_POST] = "POST", [IOT_HTTP_METHOD_PUT] = "PUT",
+ [IOT_HTTP_METHOD_DELETE] = "DELETE", [IOT_HTTP_METHOD_HEAD] = "HEAD",
+ };
+
+ rc = _http_client_parse_url(params->url, &host, &path);
+ if (rc) {
+ Log_e("http parse url failed %d", rc);
+ return rc;
+ }
+
+ buf_len = path.data_len + host.data_len + 128;
+ buf = HAL_Malloc(buf_len);
+ if (!buf) {
+ Log_e("http malloc request line failed %d", rc);
+ return QCLOUD_ERR_MALLOC;
+ }
+
+ len = HAL_Snprintf(buf, buf_len, "%s %.*s HTTP/1.1\r\nHost:%.*s\r\n", method_str[params->method], path.data_len,
+ path.data, host.data_len, host.data);
+ rc = _http_client_send(client, buf, len);
+ HAL_Free(buf);
+ return rc;
+}
+
+/**
+ * @brief Send http request content(optional).
+ *
+ * @param[in,out] client pointer to http client. @see IotHTTPClient
+ * @param[in] params params needed to send request to http server, @see IotHTTPRequestParams
+ * @return int
+ */
+static int _http_client_send_request_content(IotHTTPClient *client, const IotHTTPRequestParams *params)
+{
+ int rc = 0, len, buf_len;
+ char *buf;
+
+ buf_len = params->content_type ? 32 + strlen(params->content_type) : 32;
+ buf = HAL_Malloc(buf_len);
+ if (!buf) {
+ Log_e("http malloc request line failed %d", rc);
+ return QCLOUD_ERR_MALLOC;
+ }
+
+ len = HAL_Snprintf(buf, buf_len, "Content-Length:%d\r\n", params->content_length);
+ rc = _http_client_send(client, buf, len);
+ if (rc) {
+ goto exit;
+ }
+
+ if (params->content_type) {
+ len = HAL_Snprintf(buf, buf_len, "Content-Type:%s\r\n", params->content_type);
+ rc = _http_client_send(client, buf, len);
+ if (rc) {
+ goto exit;
+ }
+ }
+
+ rc = _http_client_send(client, "\r\n", 2);
+ if (rc) {
+ goto exit;
+ }
+
+ HAL_Free(buf);
+ return _http_client_send(client, (char *)params->content, params->content_length);
+exit:
+ HAL_Free(buf);
+ Log_e("send request content failed %d", rc);
+ return rc;
+}
+
+/**
+ * @brief Send http request to http server.
+ *
+ * @param[in,out] client pointer to http client. @see IotHTTPClient
+ * @param[in] params @see IotHTTPRequestParams
+ * @return 0 for success. others @see IotReturnCode
+ */
+static int _http_client_send_request(IotHTTPClient *client, const IotHTTPRequestParams *params)
+{
+ int rc;
+
+ // 1. send request line
+ rc = _http_client_send_request_line(client, params);
+ if (rc) {
+ Log_e("http send request line failed, rc=%d", rc);
+ return rc;
+ }
+
+ // 2. send request header
+ if (params->header) {
+ rc = _http_client_send(client, (char *)params->header, strlen(params->header));
+ if (rc) {
+ Log_e("write failed %d", rc);
+ return rc;
+ }
+ }
+ if (!params->content || !params->content_length) { // no payload body
+ return _http_client_send(client, "\r\n", 2);
+ }
+
+ // 3. send payload body
+ return _http_client_send_request_content(client, params);
+}
+
+/**************************************************************************************
+ * response
+ **************************************************************************************/
+
+/**
+ * @brief Recv data chunked.
+ *
+ * @param[in,out] client pointer to http client. @see IotHTTPClient
+ * @param[in] timeout_ms timeout for recv
+ * @return length of content data recv stored in content buf
+ */
+static int _http_client_chunked_recv(IotHTTPClient *client, uint32_t timeout_ms)
+{
+ uint8_t *buf = client->response.content_buf;
+ int buf_len = client->response.content_buf_len;
+
+ IotHTTPResponseData *response = &client->response;
+
+ char *crlf_pointer = NULL;
+ char *find_from = (char *)buf;
+ int rc = 0, read_size = 0;
+ int read_size_flag = 1;
+
+ QcloudIotTimer read_timer;
+ IOT_Timer_CountdownMs(&read_timer, timeout_ms);
+
+ do {
+ while (1) {
+ // find \r\n from find_from, start with beginning
+ crlf_pointer = strstr(find_from, "\r\n");
+ if (crlf_pointer) {
+ break;
+ }
+
+ if (IOT_Timer_Expired(&read_timer)) {
+ return 0;
+ }
+
+ // try to read more to found \r\n
+ rc = _http_client_recv(client, buf + response->recv_len, buf_len - response->recv_len, 100);
+ if (rc < 0) {
+ return response->recv_len;
+ }
+ response->recv_len += rc;
+ }
+
+ // read size before \r\n
+ if (read_size_flag) {
+ *crlf_pointer = '\0';
+ read_size = strtoul(find_from, NULL, 16);
+ response->recv_len -= crlf_pointer - find_from + 2;
+ if (!read_size) { // last chunk
+ response->need_recv_len = 0;
+ return response->recv_len;
+ }
+ memmove(find_from, crlf_pointer + 2, buf + response->recv_len - (uint8_t *)find_from);
+ read_size_flag = false;
+ continue;
+ }
+
+ // remove /r/n and set find from
+ response->recv_len -= 2;
+ memmove(crlf_pointer, crlf_pointer + 2, buf + response->recv_len - (uint8_t *)crlf_pointer);
+ find_from += read_size;
+ read_size_flag = true;
+ } while (!IOT_Timer_Expired(&read_timer));
+ return 0;
+}
+
+/**
+ * @brief Recv data according content length.
+ *
+ * @param[in,out] client pointer to http client. @see IotHTTPClient
+ * @param[in] offset offset of content buf
+ * @param[in] timeout_ms timeout for recv
+ * @return length of content data recv stored in content buf
+ */
+static int _http_client_content_length_recv(IotHTTPClient *client, int offset, uint32_t timeout_ms)
+{
+ int rc, len = 0;
+
+ uint8_t *buf = client->response.content_buf;
+ int buf_len = client->response.content_buf_len;
+
+ IotHTTPResponseData *response = &client->response;
+
+ if (response->need_recv_len <= 0) {
+ // return offset for data already received
+ return offset;
+ }
+
+ // need recv length may much longger than buffer length
+ len = response->need_recv_len > buf_len - offset ? buf_len - offset : response->need_recv_len;
+ rc = _http_client_recv(client, buf + offset, len, timeout_ms);
+ if (rc <= 0) {
+ return offset ? offset : rc;
+ }
+ response->recv_len += rc;
+ response->need_recv_len -= rc;
+ return rc + offset;
+}
+
+/**
+ * @brief Recv content data.
+ *
+ * @param[in,out] client pointer to http client. @see IotHTTPClient
+ * @param[in] offset offset of content buf
+ * @param[in] timeout_ms timeout for recv
+ * @return length of content data recv stored in content buf
+ */
+static int _http_client_recv_content(IotHTTPClient *client, int offset, uint32_t timeout_ms)
+{
+ return client->response.is_chunked ? _http_client_chunked_recv(client, timeout_ms)
+ : _http_client_content_length_recv(client, offset, timeout_ms);
+}
+
+/**
+ * @brief Recv response from http server.
+ *
+ * @param[in,out] client pointer to http client. @see IotHTTPClient
+ * @param[in] timeout_ms timeout for recv
+ * @return 0 for success. others @see IotReturnCode
+ */
+static int _http_client_recv_response(IotHTTPClient *client, uint32_t timeout_ms)
+{
+ QcloudIotTimer timer;
+ char *content_length, *body_end;
+ int rc, len = 0;
+ char *buf = (char *)client->response.content_buf;
+ int buf_len = client->response.content_buf_len;
+
+ IotHTTPResponseData *response = &client->response;
+ memset(buf, 0, buf_len);
+ IOT_Timer_CountdownMs(&timer, timeout_ms);
+
+ // 1. found body end
+ while (NULL == (body_end = strstr(buf, "\r\n\r\n"))) {
+ // timeout
+ if (IOT_Timer_Expired(&timer)) {
+ return QCLOUD_ERR_HTTP_TIMEOUT;
+ }
+ // timeout 100ms for header less than buff len
+ rc = _http_client_recv(client, (uint8_t *)buf + len, buf_len - len - 1, 100);
+ if (rc < 0) {
+ if (rc == QCLOUD_ERR_TCP_READ_TIMEOUT || rc == QCLOUD_ERR_SSL_READ_TIMEOUT) {
+ continue;
+ }
+ Log_e("read failed, rc %d", rc);
+ return rc;
+ }
+ len += rc;
+ }
+
+ body_end += 4; // \r\n\r\n
+ len = len - (body_end - buf); // body length
+
+ // 2. get response code
+ response->status_code = atoi(buf + 9);
+ switch (response->status_code) {
+ case 403:
+ return QCLOUD_ERR_HTTP_AUTH;
+ case 404:
+ return QCLOUD_ERR_HTTP_NOT_FOUND;
+ default:
+ if (response->status_code < 200 || response->status_code >= 400) {
+ Log_w("HTTP status code %d", response->status_code);
+ return QCLOUD_ERR_HTTP;
+ }
+ break;
+ }
+
+ // 3. parse header
+ // content length
+ content_length = strstr(buf, "Content-Length");
+ if (content_length) {
+ response->is_chunked = 0;
+ response->content_length = atoi(content_length + strlen("Content-Length: "));
+ response->need_recv_len = response->content_length - len;
+ goto recv_content;
+ }
+
+ // chunked
+ if (strstr(buf, "Transfer-Encoding: chunked")) {
+ response->is_chunked = 1;
+ response->need_recv_len = 1; // means always need recv
+ goto recv_content;
+ }
+
+ Log_e("Could not parse header");
+ response->content_length = -1;
+ return QCLOUD_ERR_HTTP;
+
+// 4. remove data and receive body data
+recv_content:
+ memmove(buf, body_end, len);
+ response->recv_len = len;
+ return _http_client_recv_content(client, len, IOT_Timer_Remain(&timer));
+}
+
+/**************************************************************************************
+ * API
+ **************************************************************************************/
+
+/**
+ * @brief Malloc http client.
+ *
+ * @return pointer to http client
+ */
+void *IOT_HTTP_Init(void)
+{
+ return HAL_Malloc(sizeof(IotHTTPClient));
+}
+
+/**
+ * @brief Free http client.
+ *
+ */
+void IOT_HTTP_Deinit(void *client)
+{
+ HAL_Free(client);
+}
+
+/**
+ * @brief Connect http server.
+ *
+ * @param[in,out] client pointer to http client
+ * @param[in] params params needed to connect http server, @see IotHTTPConnectParams
+ * @return 0 for success. others @see IotReturnCode
+ */
+int IOT_HTTP_Connect(void *client, IotHTTPConnectParams *params)
+{
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
+ IotHTTPClient *http_client = (IotHTTPClient *)client;
+ memset(http_client, 0, sizeof(IotHTTPClient));
+
+ int rc;
+
+ // parse url
+ HTTPData host, path;
+ rc = _http_client_parse_url(params->url, &host, &path);
+ if (rc) {
+ Log_e("http parse url failed %d", rc);
+ return rc;
+ }
+
+ // copy host
+ char *host_str = HAL_Malloc(host.data_len + 1);
+ if (!host_str) {
+ return QCLOUD_ERR_MALLOC;
+ }
+ strncpy(host_str, host.data, host.data_len);
+ host_str[host.data_len] = '\0';
+
+ // http connect
+ rc = _http_client_connect(http_client, host_str, params->port, params->ca_crt);
+ HAL_Free(host_str);
+ return rc;
+}
+
+/**
+ * @brief Request http server.
+ *
+ * @param[in,out] client pointer to http client
+ * @param[in] params params needed to send request to http server, @see IotHTTPRequestParams
+ * @return 0 for success. others @see IotReturnCode
+ */
+int IOT_HTTP_Request(void *client, IotHTTPRequestParams *params)
+{
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
+ return _http_client_send_request((IotHTTPClient *)client, params);
+}
+
+/**
+ * @brief Send data to http server.
+ *
+ * @param[in,out] client pointer to http client
+ * @param[in] data data to send
+ * @param[out] data_len data len
+ * @return 0 for success. others @see IotReturnCode
+ */
+int IOT_HTTP_Send(void *client, uint8_t *data, int data_len)
+{
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
+ return _http_client_send((IotHTTPClient *)client, (char *)data, data_len);
+}
+
+/**
+ * @brief Recv data from http server.
+ *
+ * @param[in,out] client pointer to http client
+ * @param[out] buf buffer to store recv data
+ * @param[in] buf_len buffer len
+ * @param timeout_ms timeout for recv
+ * @return >= 0 for recv data len. others @see IotReturnCode
+ */
+int IOT_HTTP_Recv(void *client, uint8_t *buf, int buf_len, uint32_t timeout_ms)
+{
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
+ IotHTTPClient *http_client = (IotHTTPClient *)client;
+ http_client->response.content_buf = buf;
+ http_client->response.content_buf_len = buf_len;
+ return http_client->response.need_recv_len ? _http_client_recv_content(http_client, 0, timeout_ms)
+ : _http_client_recv_response(http_client, timeout_ms);
+}
+
+/**
+ * @brief Check is recv finished.
+ *
+ * @param[in,out] client pointer to http client
+ * @return true for finished.
+ */
+int IOT_HTTP_IsRecvFinished(void *client)
+{
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
+ IotHTTPClient *http_client = (IotHTTPClient *)client;
+ return http_client->response.need_recv_len == 0;
+}
+
+/**
+ * @brief Disconnect http server.
+ *
+ * @param[in,out] client pointer to http client
+ */
+void IOT_HTTP_Disconnect(void *client)
+{
+ POINTER_SANITY_CHECK_RTN(client);
+ _http_client_disconnect((IotHTTPClient *)client);
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/http_signed/CMakeLists.txt b/components/connectivity/iot-hub-device-c-sdk/services/common/http_signed/CMakeLists.txt
new file mode 100644
index 00000000..65da947b
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/http_signed/CMakeLists.txt
@@ -0,0 +1,2 @@
+file(GLOB src_http_signed ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
+set(src_services ${src_services} ${src_http_signed} PARENT_SCOPE)
\ No newline at end of file
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/http_signed/src/http_signed.c b/components/connectivity/iot-hub-device-c-sdk/services/common/http_signed/src/http_signed.c
new file mode 100644
index 00000000..8d9be52a
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/http_signed/src/http_signed.c
@@ -0,0 +1,279 @@
+/**
+ * @file http_signed.c
+ * @author {hubert} ({hubertxxu@tencent.com})
+ * @brief
+ * @version 1.0
+ * @date 2022-01-21
+ *
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-01-21 1.0 hubertxxu first commit
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "qcloud_iot_http_signed.h"
+
+#include "utils_hmac.h"
+#include "utils_base64.h"
+
+/**
+ * @brief http signed request header.
+ *
+ */
+#define HTTP_SIGNED_REQUEST_HEADER_LEN (1024)
+
+/**
+ * @brief http signed request header signed length
+ *
+ */
+#define HTTP_SIGNED_STRING_BUFFER_LEN (256)
+
+/**
+ * @brief http signed request url length
+ *
+ */
+#define HTTP_SIGNED_URL_LEN (128)
+
+#define QCLOUD_SHA256_RESULT_LEN (32)
+#define QCLOUD_SHA1_RESULT_LEN (20)
+
+typedef struct {
+ HttpSignedParams params;
+ void *http_client;
+ IotHTTPRequestParams http_request;
+ char *sign_string;
+ char *url;
+} HTTPSignedHandle;
+
+/**
+ * @brief get rand
+ *
+ * @param[in, out] seed
+ * @return int
+ */
+static int _get_http_header_nonce(void)
+{
+ srand(IOT_Timer_CurrentSec());
+ return rand();
+}
+
+/**
+ * @brief Connect http server.
+ *
+ * @param[in,out] handle pointer to http signed handle, @see HTTPSignedHandle
+ * @return 0 for success. others @see IotReturnCode
+ */
+static int _http_signed_connect(HTTPSignedHandle *handle)
+{
+ IotHTTPConnectParams connect_params = {
+ .url = handle->url,
+ .port = "80",
+ .ca_crt = NULL, // TODO: support cert
+
+ };
+ return IOT_HTTP_Connect(handle->http_client, &connect_params);
+}
+
+/**
+ * @brief http signed header construct
+ *
+ * @param[in,out] handle pointer to http signed handle, @see HTTPSignedHandle
+ * @param request_body_buf
+ * @param request_body_buf_len
+ * @return 0 if success
+ */
+static void _http_signed_upload_header_construct(HTTPSignedHandle *handle, const char *request_body_buf,
+ int request_body_buf_len)
+{
+/**
+ * @brief http signed header format
+ *
+ */
+#define QCLOUD_HTTP_HEADER_FORMAT \
+ "Accept: %s*/*\r\n" \
+ "X-TC-Algorithm: %s\r\n" \
+ "X-TC-Timestamp: %d\r\n" \
+ "X-TC-Nonce: %d\r\n" \
+ "X-TC-Signature: %s\r\n"
+
+#define QCLOUD_SUPPORT_HMACSHA1 "hmacsha1"
+#define QCLOUD_SUPPORT_RSASHA256 "rsa-sha256"
+#define QCLOUD_SUPPORT_ACCEPT_HEADER "application/json;"
+
+ char sign[QCLOUD_SHA1_RESULT_LEN] = {0};
+ char signout[QCLOUD_SHA1_RESULT_LEN * 2] = {0};
+ char request_buf_sha1[QCLOUD_SHA1_RESULT_LEN * 2 + 1] = {0};
+ size_t olen = 0;
+ int nonce = _get_http_header_nonce();
+ uint32_t timestamp = IOT_Timer_CurrentSec();
+
+ memset(handle->sign_string, 0, HTTP_SIGNED_STRING_BUFFER_LEN);
+ /* cal hmac sha1 */
+ utils_sha1_hex((const uint8_t *)request_body_buf, request_body_buf_len, (uint8_t *)request_buf_sha1);
+ /* create sign string */
+ HAL_Snprintf(handle->sign_string, HTTP_SIGNED_STRING_BUFFER_LEN, "%s\n%s\n%s\n%s\n%s\n%d\n%d\n%s", "POST",
+ handle->params.host, handle->params.uri, "", QCLOUD_SUPPORT_HMACSHA1, timestamp, nonce,
+ request_buf_sha1);
+
+ utils_hmac_sha1(handle->sign_string, strlen(handle->sign_string), (uint8_t *)handle->params.secretkey,
+ strlen(handle->params.secretkey), sign);
+
+ /* base64 encode */
+ utils_base64encode((uint8_t *)signout, QCLOUD_SHA1_RESULT_LEN * 2, &olen, (const uint8_t *)sign,
+ QCLOUD_SHA1_RESULT_LEN);
+
+ HAL_Snprintf(handle->http_request.header, HTTP_SIGNED_REQUEST_HEADER_LEN, QCLOUD_HTTP_HEADER_FORMAT,
+ QCLOUD_SUPPORT_ACCEPT_HEADER, QCLOUD_SUPPORT_HMACSHA1, timestamp, nonce, signout);
+}
+
+/**
+ * @brief http signed init.
+ *
+ * @param params @see HttpSignedParams
+ * @return pointer to http signed @see HTTPSignedHandle
+ */
+void *_http_signed_init(HttpSignedParams *params)
+{
+ HTTPSignedHandle *handle = HAL_Malloc(sizeof(HTTPSignedHandle));
+ if (!handle) {
+ goto exit;
+ }
+
+ handle->http_client = IOT_HTTP_Init();
+ if (!handle->http_client) {
+ goto exit;
+ }
+
+ handle->http_request.header = HAL_Malloc(HTTP_SIGNED_REQUEST_HEADER_LEN);
+ if (!handle->http_request.header) {
+ goto exit;
+ }
+
+ handle->sign_string = HAL_Malloc(HTTP_SIGNED_STRING_BUFFER_LEN);
+ if (!handle->sign_string) {
+ goto exit;
+ }
+
+ handle->url = HAL_Malloc(HTTP_SIGNED_URL_LEN);
+ if (!handle->url) {
+ goto exit;
+ }
+
+ handle->params = *params;
+ memset(handle->url, 0, HTTP_SIGNED_URL_LEN);
+ HAL_Snprintf(handle->url, HTTP_SIGNED_URL_LEN, "%s://%s%s", "http", handle->params.host, handle->params.uri);
+
+ return handle;
+exit:
+ if (handle) {
+ HAL_Free(handle->http_request.header);
+ HAL_Free(handle->sign_string);
+ HAL_Free(handle->url);
+ IOT_HTTP_Deinit(handle->http_client);
+ HAL_Free(handle);
+ }
+ return NULL;
+}
+
+/**
+ * @brief send to server
+ *
+ * @param[in] handle pointer to http signed @see HTTPSignedHandle
+ * @param[in] upload_buf need send data
+ * @param[in] upload_len send length
+ * @return int 0 for success. others @see IotReturnCode
+ */
+int _http_signed_upload(HTTPSignedHandle *handle, const char *upload_buf, size_t upload_len)
+{
+ int rc = 0;
+
+ rc = _http_signed_connect(handle);
+ if (rc) {
+ return rc;
+ }
+ _http_signed_upload_header_construct(handle, upload_buf, upload_len);
+
+ handle->http_request.url = handle->url;
+ handle->http_request.method = IOT_HTTP_METHOD_POST;
+ handle->http_request.content_type = "application/json;charset=utf-8";
+ handle->http_request.content = (char *)upload_buf;
+ handle->http_request.content_length = upload_len;
+
+ rc = IOT_HTTP_Request(handle->http_client, &handle->http_request);
+ return rc;
+}
+
+/**
+ * @brief http signed deinit
+ *
+ * @param[in] handle pointer to http signed @see HTTPSignedHandle
+ */
+static void _http_signed_deinit(HTTPSignedHandle *signed_handle)
+{
+ POINTER_SANITY_CHECK_RTN(signed_handle);
+ IOT_HTTP_Disconnect(signed_handle->http_client);
+ IOT_HTTP_Deinit(signed_handle->http_client);
+ HAL_Free(signed_handle->http_request.header);
+ HAL_Free(signed_handle->sign_string);
+ HAL_Free(signed_handle->url);
+ HAL_Free(signed_handle);
+}
+
+/**
+ * @brief post message and recv response
+ *
+ * @param params @see HttpSignedParams
+ * @param request_buf request buffer
+ * @param request_buf_len request buffer length
+ * @param response_buf response buffer if need recv
+ * @param response_buf_len response buffer length if need recv
+ * @return int if need recv return recv length else return 0 is success
+ */
+int IOT_HTTP_SignedRequest(HttpSignedParams *params, const char *request_buf, size_t request_buf_len,
+ uint8_t *response_buf, int response_buf_len)
+{
+ int rc = 0;
+ POINTER_SANITY_CHECK(params, QCLOUD_ERR_INVAL);
+ POINTER_SANITY_CHECK(request_buf, QCLOUD_ERR_INVAL);
+ if (params->need_recv) {
+ POINTER_SANITY_CHECK(response_buf, QCLOUD_ERR_INVAL);
+ }
+ HTTPSignedHandle *handle = _http_signed_init(params);
+ if (!handle) {
+ Log_e("http signed init error.");
+ goto exit;
+ }
+ rc = _http_signed_upload(handle, request_buf, request_buf_len);
+ if (rc) {
+ Log_e("http signed upload error.");
+ goto exit;
+ }
+ if (params->need_recv) {
+ rc = IOT_HTTP_Recv(handle->http_client, response_buf, response_buf_len, params->recv_timeout_ms);
+ }
+
+exit:
+ _http_signed_deinit(handle);
+ return rc;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/CMakeLists.txt b/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/CMakeLists.txt
new file mode 100644
index 00000000..e3a2f1ba
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/CMakeLists.txt
@@ -0,0 +1,16 @@
+file(GLOB src_log_upload ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
+set(inc_log_upload ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
+
+set(src_services ${src_services} ${src_log_upload} PARENT_SCOPE)
+set(inc_services ${inc_services} ${inc_log_upload} PARENT_SCOPE)
+
+if( ${CONFIG_COMPILE_SAMPLE} STREQUAL "ON")
+ file(GLOB src_log_upload_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/log_upload_sample.c)
+ add_executable(log_upload_sample ${src_log_upload_sample})
+ target_link_libraries(log_upload_sample ${libsdk})
+endif()
+
+if( ${CONFIG_IOT_TEST} STREQUAL "ON")
+ file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
+ set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
+endif()
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/inc/log_upload.h b/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/inc/log_upload.h
new file mode 100644
index 00000000..b683be0c
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/inc/log_upload.h
@@ -0,0 +1,213 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file mqtt_client.h
+ * @brief mqtt client internel api
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-31
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-31 1.0 fancyxu first commit
+ * 2021-07-08 1.1 fancyxu fix code standard of IotReturnCode and QcloudIotClient
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_LOG_UPLOAD_INC_LOG_UPLOAD_H_
+#define IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_LOG_UPLOAD_INC_LOG_UPLOAD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "qcloud_iot_log_upload.h"
+
+#include "utils_list.h"
+#include "utils_base64.h"
+#include "utils_hmac.h"
+
+/**
+ * @brief To check log http server return msg or not.
+ *
+ */
+#define LOG_CHECK_HTTP_RET_CODE
+
+/**
+ * @brief Enable log upload debug or not.
+ *
+ */
+// #define LOG_UPLOAD_DEBUG
+
+/**
+ * @brief Http recv length.
+ *
+ */
+#define HTTP_RET_JSON_LENGTH 256
+
+/**
+ * @brief Wait http recv time out.
+ *
+ */
+#define HTTP_WAIT_RET_TIMEOUT_MS 5000
+
+/**
+ * @brief Do immediate log update if buffer is lower than this threshold
+ *
+ */
+#define LOG_LOW_BUFFER_THRESHOLD (LOG_UPLOAD_BUFFER_SIZE / 4)
+
+/**
+ * @brief Sign key length.
+ *
+ */
+#define SIGN_KEY_SIZE (24)
+
+#ifdef LOG_UPLOAD_DEBUG
+#define UPLOAD_DBG(fmt, ...) HAL_Printf(">>LOG-DBG>>%s(%d): " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#else
+#define UPLOAD_DBG(...)
+#endif
+#define UPLOAD_ERR(fmt, ...) HAL_Printf(">>LOG-ERR>>%s(%d): " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
+
+/**
+ * @brief Log upload buffer init.
+ *
+ * @param[in] init_params @see LogUploadInitParams
+ * @return void* if success return @see LogUploadBuffer
+ */
+void *log_upload_buffer_init(const LogUploadInitParams *init_params);
+
+/**
+ * @brief Deinit log buffer handle.
+ *
+ * @param[in] upload_buffer_handle @see LogUploadBuffer
+ */
+void log_upload_buffer_deinit(void *upload_buffer_handle);
+
+/**
+ * @brief Clear log upload buffer.
+ *
+ */
+void log_upload_buffer_clear(void);
+
+/**
+ * @brief Get the log buffer write index object.
+ *
+ * @param[in] log_upload_buffer_handle @see LogUploadBuffer
+ * @return write index
+ */
+uint32_t log_upload_buffer_get_write_index(void *log_upload_buffer_handle);
+
+/**
+ * @brief Get the log buffer head len object.
+ *
+ * @param[in] log_upload_buffer_handle @see LogUploadBuffer
+ * @return head length
+ */
+uint32_t log_upload_buffer_get_head_len(void *log_upload_buffer_handle);
+
+/**
+ * @brief Get the log buffer size object.
+ *
+ * @return log buffer size
+ */
+uint32_t log_upload_buffer_get_size(void);
+
+/**
+ * @brief Check log buffer is empty or not.
+ *
+ * @param[in] log_upload_buffer_handle @see LogUploadBuffer
+ * @return true empty
+ * @return false not empty
+ */
+bool log_upload_buffer_is_empty(void *log_upload_buffer_handle);
+
+/**
+ * @brief When a new log appears during the reporting period, move the log to the specified position in the buffer.
+ *
+ * @param[in] log_upload_buffer_handle @see LogUploadBuffer
+ * @param[in] need_remove_size
+ */
+void log_upload_buffer_remove_log(void *log_upload_buffer_handle, uint32_t need_remove_size);
+
+/**
+ * @brief Copy log buffer head.
+ *
+ * @param[out] dst Destination address
+ * @param[in] log_upload_buffer_handle @see LogUploadBuffer
+ */
+void log_upload_buffer_copy_header(char *dst, void *log_upload_buffer_handle);
+
+/**
+ * @brief Get the log buffer object.
+ *
+ * @param[in] log_upload_buffer_handle @see LogUploadBuffer
+ * @return log buffer
+ */
+char *log_upload_buffer_get(void *log_upload_buffer_handle);
+
+/**
+ * @brief Push data to log buffer.
+ *
+ * @param[in] log_content need push data
+ * @return if 0 success else QCLOUD_ERR_FAILURE
+ */
+int log_upload_buffer_append(const char *log_content);
+
+/**
+ * @brief Post data to tencent cloud over http.
+ *
+ * @param[in] post_buf need post data buffer
+ * @param[in] post_size need post data buffer length
+ * @return @see IotReturnCode
+ */
+int post_one_http_to_server(void *log_upload_buffer_handle, char *post_buf, size_t post_size);
+
+/**
+ * @brief Set log upload in comm err state.
+ *
+ * @param state true or false
+ */
+void log_upload_set_upload_log_in_comm_err(bool state);
+
+/**
+ * @brief Change log upload level.
+ *
+ * @param[in] level @see LogLevel
+ */
+void log_upload_set_log_upload_level(LogLevel level);
+
+/**
+ * @brief Get log upload level.
+ *
+ * @return @see LogLevel
+ */
+LogLevel log_upload_get_log_upload_level(void);
+
+/**
+ * @brief Init log upload module.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return @see IotReturnCode
+ */
+int log_mqtt_init(void *client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_LOG_UPLOAD_INC_LOG_UPLOAD_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/sample/log_upload_sample.c b/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/sample/log_upload_sample.c
new file mode 100644
index 00000000..96ea1b04
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/sample/log_upload_sample.c
@@ -0,0 +1,211 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file broadcast_sample.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-07-18
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-07-18 1.0 fancyxu first commit
+ *
+ */
+
+#include
+#include
+#include
+#include
+
+#include "qcloud_iot_common.h"
+
+#include "utils_log.h"
+
+/**
+ * @brief MQTT event callback, @see MQTTEventHandleFun
+ *
+ * @param[in] client pointer to mqtt client
+ * @param[in] handle_context context
+ * @param[in] msg msg
+ */
+static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
+{
+ MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
+ uintptr_t packet_id = (uintptr_t)msg->msg;
+
+ switch (msg->event_type) {
+ case MQTT_EVENT_UNDEF:
+ Log_i("undefined event occur.");
+ break;
+
+ case MQTT_EVENT_DISCONNECT:
+ Log_i("MQTT disconnect.");
+ break;
+
+ case MQTT_EVENT_RECONNECT:
+ Log_i("MQTT reconnect.");
+ break;
+
+ case MQTT_EVENT_PUBLISH_RECEIVED:
+ Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
+ mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
+ mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
+ break;
+ case MQTT_EVENT_SUBSCRIBE_SUCCESS:
+ Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
+ Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_SUBSCRIBE_NACK:
+ Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
+ Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
+ Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_UNSUBSCRIBE_NACK:
+ Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_PUBLISH_SUCCESS:
+ Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_PUBLISH_TIMEOUT:
+ Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_PUBLISH_NACK:
+ Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
+ break;
+ default:
+ Log_i("Should NOT arrive here.");
+ break;
+ }
+}
+
+/**
+ * @brief Setup MQTT construct parameters.
+ *
+ * @param[in,out] initParams @see MQTTInitParams
+ * @param[in] device_info @see DeviceInfo
+ */
+static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
+{
+ init_params->device_info = device_info;
+ init_params->event_handle.h_fp = _mqtt_event_handler;
+}
+
+static void _setup_log_upload_init_params(LogUploadInitParams *init_params, DeviceInfo *device_info)
+{
+ init_params->device_name = device_info->device_name;
+ init_params->product_id = device_info->product_id;
+ init_params->sign_key = device_info->device_secret;
+
+ init_params->log_buffer_size = LOG_UPLOAD_BUFFER_SIZE;
+ init_params->save_log_filename = "./tmp/upload-fail-save.log";
+ init_params->read_func = HAL_File_Read;
+ init_params->save_func = HAL_File_Write;
+ init_params->del_func = HAL_File_Del;
+ init_params->get_size_func = HAL_File_GetSize;
+}
+// ----------------------------------------------------------------------------
+// Main
+// ----------------------------------------------------------------------------
+static int sg_main_exit = 0;
+
+#ifdef __linux__
+
+#include
+#include
+#include
+
+static void _main_exit(int sig)
+{
+ Log_e("demo exit by signal:%d\n", sig);
+ sg_main_exit = 1;
+}
+#endif
+
+int main(int argc, char **argv)
+{
+#ifdef __linux__
+ signal(SIGINT, _main_exit);
+#endif
+
+ int rc;
+ int loop_cnt = 5;
+ // init log level
+ LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
+ func.log_upload = IOT_Log_Upload_AppendToUploadBuffer;
+
+ utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
+
+ DeviceInfo device_info;
+
+ rc = HAL_GetDevInfo((void *)&device_info);
+ if (rc) {
+ Log_e("get device info failed: %d", rc);
+ return rc;
+ }
+
+ // init connection
+ MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
+ _setup_connect_init_params(&init_params, &device_info);
+ // init log upload
+ LogUploadInitParams log_upload_init_params = DEFAULT_LOG_UPLOAD_INIT_PARAMS;
+
+ _setup_log_upload_init_params(&log_upload_init_params, &device_info);
+
+ IOT_Log_Upload_InitPre(&log_upload_init_params);
+ // create MQTT client and connect with server
+ void *client = IOT_MQTT_Construct(&init_params);
+ if (client) {
+ Log_i("Cloud Device Construct Success");
+ rc = IOT_Log_Upload_Init(client);
+ if (rc) {
+ Log_e("Log upload init failed: %d", rc);
+ }
+ IOT_Log_Upload(true);
+ } else {
+ Log_e("MQTT Construct failed!");
+ return QCLOUD_ERR_FAILURE;
+ }
+
+ do {
+ Log_d("log upload test debug %d...", loop_cnt);
+ Log_i("log upload test info %d...", loop_cnt);
+ Log_w("log upload test waring %d...", loop_cnt);
+ Log_e("log upload test error %d...", loop_cnt);
+ rc = IOT_MQTT_Yield(client, 200);
+ IOT_Log_Upload(rc ? true : false);
+ } while (loop_cnt--);
+ // If you have to report some urgent messages, you can call this function
+ IOT_Log_Upload(true);
+ rc = IOT_MQTT_Destroy(&client);
+ IOT_Log_Upload_Deinit();
+ utils_log_deinit();
+ return rc;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/src/log_buffer.c b/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/src/log_buffer.c
new file mode 100644
index 00000000..ea52bfdb
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/src/log_buffer.c
@@ -0,0 +1,735 @@
+/**
+ * @file log_buffer.c
+ * @author {hubert} ({hubertxxu@tencent.com})
+ * @brief
+ * @version 1.0
+ * @date 2022-01-23
+ *
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-01-23 1.0 hubertxxu first commit
+ *
+ */
+
+#include "log_upload.h"
+#include "utils_hmac.h"
+
+typedef struct {
+ void *lock_buf;
+ char *log_buffer;
+ const char *host;
+ uint32_t log_buffer_size;
+ uint32_t log_buffer_head_len;
+ uint32_t write_index;
+ char sign_key[SIGN_KEY_SIZE + 1];
+} LogUploadBuffer;
+
+static LogUploadBuffer *sg_log_upload_buffer_handle = NULL;
+
+// ----------------------------------------------------------------------------
+// Common
+// ----------------------------------------------------------------------------
+
+/**
+ * @brief Reset log upload buffer.
+ *
+ * @param[in,out] log_upload_buffer_handle handle of log upload buffer
+ */
+static void _reset_log_buffer(LogUploadBuffer *log_upload_buffer_handle)
+{
+ log_upload_buffer_handle->write_index = log_upload_buffer_handle->log_buffer_head_len;
+ memset(log_upload_buffer_handle->log_buffer + log_upload_buffer_handle->log_buffer_head_len, 0,
+ log_upload_buffer_handle->log_buffer_size - log_upload_buffer_handle->log_buffer_head_len);
+}
+
+/**
+ * @brief Init log buffer common logic.
+ *
+ * @param[in] init_params @see LogUploadInitParams
+ * @return pointer to log buffer
+ */
+static LogUploadBuffer *_log_upload_buffer_init_pre(const LogUploadInitParams *init_params)
+{
+ LogUploadBuffer *handle = (LogUploadBuffer *)HAL_Malloc(sizeof(LogUploadBuffer));
+ if (!handle) {
+ UPLOAD_ERR("log upload buffer handle malloc error.");
+ goto err_exit;
+ }
+
+ memset(handle, 0, sizeof(LogUploadBuffer));
+
+ handle->lock_buf = HAL_MutexCreate();
+ if (!handle->lock_buf) {
+ UPLOAD_ERR("mutex create failed");
+ goto err_exit;
+ }
+ handle->log_buffer_size = init_params->log_buffer_size;
+ handle->log_buffer = HAL_Malloc(handle->log_buffer_size);
+ if (!handle->log_buffer) {
+ UPLOAD_ERR("malloc log buffer failed");
+ goto err_exit;
+ }
+ memset(handle->log_buffer, 0, handle->log_buffer_size);
+
+ /*init sign key*/
+ int key_len = strlen(init_params->sign_key);
+ if (!key_len) {
+ UPLOAD_ERR("invalid sign key length");
+ goto err_exit;
+ }
+ memcpy(handle->sign_key, init_params->sign_key, key_len > SIGN_KEY_SIZE ? SIGN_KEY_SIZE : key_len);
+
+ sg_log_upload_buffer_handle = handle;
+ return handle;
+err_exit:
+ if (handle) {
+ HAL_MutexDestroy(handle->lock_buf);
+ HAL_Free(handle);
+ }
+ return NULL;
+}
+
+/**
+ * @brief Deinit log buffer handle.
+ *
+ * @param[in] upload_buffer_handle @see LogUploadBuffer
+ */
+void log_upload_buffer_deinit(void *upload_buffer_handle)
+{
+ LogUploadBuffer *handle = (LogUploadBuffer *)upload_buffer_handle;
+ if (!handle) {
+ return;
+ }
+ HAL_MutexLock(handle->lock_buf);
+ _reset_log_buffer(handle);
+ HAL_Free(handle->log_buffer);
+ handle->log_buffer = NULL;
+ HAL_MutexUnlock(handle->lock_buf);
+ HAL_MutexDestroy(handle->lock_buf);
+ handle->lock_buf = NULL;
+ HAL_Free(handle);
+ sg_log_upload_buffer_handle = NULL;
+}
+
+/**
+ * @brief Get the log buffer write index object.
+ *
+ * @param[in] log_upload_buffer_handle @see LogUploadBuffer
+ * @return write index
+ */
+uint32_t log_upload_buffer_get_write_index(void *log_upload_buffer_handle)
+{
+ LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
+ HAL_MutexLock(handle->lock_buf);
+ uint32_t write_index = handle->write_index;
+ HAL_MutexUnlock(handle->lock_buf);
+ return write_index;
+}
+
+/**
+ * @brief Get the log buffer object.
+ *
+ * @param[in] log_upload_buffer_handle @see LogUploadBuffer
+ * @return log buffer
+ */
+char *log_upload_buffer_get(void *log_upload_buffer_handle)
+{
+ LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
+ return handle->log_buffer;
+}
+
+/**
+ * @brief Get the log buffer head len object.
+ *
+ * @param[in] log_upload_buffer_handle @see LogUploadBuffer
+ * @return head length
+ */
+uint32_t log_upload_buffer_get_head_len(void *log_upload_buffer_handle)
+{
+ LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
+ return handle->log_buffer_head_len;
+}
+
+/**
+ * @brief Check log buffer is empty or not.
+ *
+ * @param[in] log_upload_buffer_handle @see LogUploadBuffer
+ * @return true empty
+ * @return false not empty
+ */
+bool log_upload_buffer_is_empty(void *log_upload_buffer_handle)
+{
+ LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
+ return handle->write_index == handle->log_buffer_head_len;
+}
+
+/**
+ * @brief When a new log appears during the reporting period, move the log to the specified position in the buffer.
+ *
+ * @param[in] log_upload_buffer_handle @see LogUploadBuffer
+ * @param[in] need_remove_size
+ */
+void log_upload_buffer_remove_log(void *log_upload_buffer_handle, uint32_t need_remove_size)
+{
+ LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
+ if (need_remove_size > handle->write_index || need_remove_size > handle->log_buffer_size) {
+ return;
+ }
+ HAL_MutexLock(handle->lock_buf);
+ memmove(handle->log_buffer + handle->log_buffer_head_len, handle->log_buffer + need_remove_size,
+ handle->write_index - need_remove_size);
+ handle->write_index = handle->write_index - need_remove_size + handle->log_buffer_head_len;
+ memset(handle->log_buffer + handle->write_index, 0, handle->log_buffer_size - handle->write_index);
+ HAL_MutexUnlock(handle->lock_buf);
+}
+
+/**
+ * @brief Copy log buffer head.
+ *
+ * @param[out] dst Destination address
+ * @param[in] log_upload_buffer_handle @see LogUploadBuffer
+ */
+void log_upload_buffer_copy_header(char *dst, void *log_upload_buffer_handle)
+{
+ LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
+ memcpy(dst, handle->log_buffer, handle->log_buffer_head_len);
+}
+
+/**
+ * @brief Clear log upload buffer.
+ *
+ */
+void log_upload_buffer_clear(void)
+{
+ if (!sg_log_upload_buffer_handle) {
+ UPLOAD_ERR("log upload buffer not init.");
+ return;
+ }
+ HAL_MutexLock(sg_log_upload_buffer_handle->lock_buf);
+ _reset_log_buffer(sg_log_upload_buffer_handle);
+ HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
+}
+
+/**
+ * @brief Get the log buffer size object.
+ *
+ * @return log buffer size
+ */
+uint32_t log_upload_buffer_get_size(void)
+{
+ if (!sg_log_upload_buffer_handle) {
+ UPLOAD_ERR("log upload buffer not init.");
+ return 0;
+ }
+ return sg_log_upload_buffer_handle->log_buffer_size;
+}
+
+// ----------------------------------------------------------------------------
+// New log fromat by json
+// ----------------------------------------------------------------------------
+#ifdef LOG_UPLOAD_TYPE_JSON
+/**
+ * @brief Log upload buffer init.
+ *
+ * @param[in] init_params @see LogUploadInitParams
+ * @return void* if success return @see LogUploadBuffer
+ */
+void *log_upload_buffer_init(const LogUploadInitParams *init_params)
+{
+ LogUploadBuffer *handle = _log_upload_buffer_init_pre(init_params);
+ if (!handle) {
+ return NULL;
+ }
+ handle->log_buffer_head_len = HAL_Snprintf(handle->log_buffer, handle->log_buffer_size,
+ "{\"DeviceName\":\"%s\",\"ProductId\":\"%s\",\"Message\":[",
+ init_params->device_name, init_params->product_id);
+ handle->write_index = handle->log_buffer_head_len;
+ handle->host = init_params->host;
+ _reset_log_buffer(handle);
+ return handle;
+}
+
+#ifdef LOG_CHECK_HTTP_RET_CODE
+/**
+ * @brief Get http respose result.
+ *
+ * @param[in] json http response buffer
+ * @param[in] json_len http response buffer length
+ * @return true log report success
+ * @return false log report error
+ */
+static bool _get_json_ret_code(char *json, size_t json_len)
+{
+ int rc = 0;
+ UtilsJsonValue resposse;
+ // get response
+ rc = utils_json_value_get("Response.Error", strlen("Response.Error"), json, json_len, &resposse);
+ if (rc) {
+ return true;
+ }
+ return false;
+}
+#endif
+
+/**
+ * @brief When upload, append endchar in the upload buffer ending.
+ *
+ * @param[in,out] log_buf upload buffer
+ * @param[in] log_size upload buffer index
+ */
+static void _append_endchar_to_upload_buffer(char *log_buf, size_t log_size)
+{
+ log_buf[log_size - 2] = ']';
+ log_buf[log_size - 1] = '}';
+}
+
+/**
+ * @brief When upload, remove endchar in the upload buffer ending.
+ *
+ * @param[in,out] log_buf upload buffer
+ * @param[in] log_size upload buffer index
+ */
+static void _remove_endchar_from_upload_buffer(char *log_buf, size_t log_size)
+{
+ log_buf[log_size - 2] = ',';
+ log_buf[log_size - 1] = ' ';
+}
+
+/**
+ * @brief Post data to tencent cloud over http.
+ *
+ * @param[in] post_buf need post data buffer
+ * @param[in] post_size need post data buffer length
+ * @return @see IotReturnCode
+ */
+int post_one_http_to_server(void *log_upload_buffer_handle, char *post_buf, size_t post_size)
+{
+ int rc = 0;
+ LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
+ if (!post_size) {
+ return 0;
+ }
+
+ _append_endchar_to_upload_buffer(post_buf, post_size);
+
+ HttpSignedParams params = {
+ .host = handle->host ? handle->host : LOG_UPLOAD_SERVER_URL,
+ .uri = LOG_UPLOAD_URI_PATH,
+ .secretkey = handle->sign_key,
+#ifdef LOG_CHECK_HTTP_RET_CODE
+ .need_recv = true,
+ .recv_timeout_ms = HTTP_WAIT_RET_TIMEOUT_MS,
+#else
+ .need_recv = false,
+ .recv_timeout_ms = 0,
+#endif
+ };
+
+#ifdef LOG_CHECK_HTTP_RET_CODE
+ char buf[HTTP_RET_JSON_LENGTH] = {0};
+ rc = IOT_HTTP_SignedRequest(¶ms, post_buf, post_size, (uint8_t *)buf, HTTP_RET_JSON_LENGTH);
+ if (rc < 0) {
+ UPLOAD_ERR("IOT_HTTP_SignedRequest failed, rc = %d", rc);
+ goto exit;
+ }
+
+ if (rc == 0) {
+ UPLOAD_ERR("HTTP Recv length = %d", rc);
+ rc = QCLOUD_ERR_HTTP;
+ goto exit;
+ }
+
+ buf[rc - 1] = '\0'; // json_parse relies on a string
+ if (strlen(buf) > 0 && _get_json_ret_code(buf, rc)) {
+ UPLOAD_DBG("Log server return SUCCESS: %s", buf);
+ rc = QCLOUD_RET_SUCCESS;
+ } else {
+ UPLOAD_ERR("Log server return FAIL: %s", buf);
+ rc = QCLOUD_ERR_HTTP;
+ }
+
+#else
+ rc = IOT_HTTP_SignedRequest(¶ms, post_buf, post_size, NULL, 0);
+#endif
+
+exit:
+ _remove_endchar_from_upload_buffer(post_buf, post_size);
+ UPLOAD_DBG("%ld data have be post to server. rc : %d", post_size, rc);
+ return rc;
+}
+
+/**
+ * @brief Push data to log buffer.
+ *
+ * @param[in] log_content need push data
+ * @return if 0 success else QCLOUD_ERR_FAILURE
+ */
+int log_upload_buffer_append(const char *log_content)
+{
+ uint16_t log_size = strlen(log_content);
+ if (HAL_MutexTryLock(sg_log_upload_buffer_handle->lock_buf) != 0) {
+ UPLOAD_ERR("trylock buffer failed!");
+ return QCLOUD_ERR_FAILURE;
+ }
+
+ if ((sg_log_upload_buffer_handle->write_index + log_size + 5) > sg_log_upload_buffer_handle->log_buffer_size) {
+ HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
+ UPLOAD_ERR("log upload buffer is not enough!");
+ return QCLOUD_ERR_FAILURE;
+ }
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\"';
+ int index = 0;
+ while (index < (log_size - 2)) {
+ // replace may overflow buffer size
+ if ((sg_log_upload_buffer_handle->write_index + 5) > sg_log_upload_buffer_handle->log_buffer_size) {
+ break;
+ }
+
+ switch (log_content[index]) {
+ case '\"':
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\"';
+ break;
+ case '\\':
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
+ break;
+ case '/':
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '/';
+ break;
+ case '\b':
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 'b';
+ break;
+ case '\f':
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 'f';
+ break;
+ case '\n':
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 'n';
+ break;
+ case '\r':
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 'r';
+ break;
+ case '\t':
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 't';
+ break;
+ default:
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] =
+ log_content[index];
+ }
+ index++;
+ }
+
+ /* replace \r\n to \", add as delimiter */
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\"';
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = ',';
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = ' ';
+
+ HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
+ return QCLOUD_RET_SUCCESS;
+}
+
+#else
+// ----------------------------------------------------------------------------
+// Old log format by text
+// ----------------------------------------------------------------------------
+
+/* log post header format */
+#define TIMESTAMP_SIZE (10)
+#define SIGNATURE_SIZE (40)
+#define CTRL_BYTES_SIZE (4)
+// LOG_BUF_FIXED_HEADER_SIZE = 112
+#define LOG_BUF_FIXED_HEADER_SIZE \
+ (SIGNATURE_SIZE + CTRL_BYTES_SIZE + MAX_SIZE_OF_PRODUCT_ID + MAX_SIZE_OF_DEVICE_NAME + TIMESTAMP_SIZE)
+
+#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
+#include "mbedtls/aes.h"
+
+/**
+ * @brief Header of encrypt.
+ *
+ */
+static char sg_head_content[128] = {0};
+
+/**
+ * @brief Log data encrypt by aes cbc.
+ *
+ * @param[in] handle @see LogUploadBuffer
+ * @param[in] buf need encrypt data
+ * @param[in] datalen need encrypt data length
+ * @return @see IotReturnCode
+ */
+static int _log_aes_cbc_encrypt(LogUploadBuffer *handle, uint8_t *buf, uint32_t datalen)
+{
+ int ret = QCLOUD_RET_SUCCESS;
+
+ mbedtls_aes_context ctx;
+
+ unsigned char iv[16];
+ memset(iv, '0', 16);
+
+ mbedtls_aes_init(&ctx);
+
+ ret = mbedtls_aes_setkey_enc(&ctx, (uint8_t *)handle->sign_key, 128);
+ if (ret) {
+ UPLOAD_ERR("Set encry key err,ret:%d", ret);
+ ret = QCLOUD_ERR_FAILURE;
+ goto exit;
+ }
+
+ ret = mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_ENCRYPT, datalen, iv, buf, buf);
+ if (ret) {
+ Log_e("encrypt err,ret:%d", ret);
+ ret = QCLOUD_ERR_FAILURE;
+ goto exit;
+ }
+
+ mbedtls_aes_free(&ctx);
+ return QCLOUD_RET_SUCCESS;
+
+exit:
+ mbedtls_aes_free(&ctx);
+ return ret;
+}
+#endif
+
+/**
+ * @brief Log upload buffer init.
+ *
+ * @param[in] init_params @see LogUploadInitParams
+ * @return void* if success return @see LogUploadBuffer
+ */
+void *log_upload_buffer_init(const LogUploadInitParams *init_params)
+{
+ LogUploadBuffer *handle = _log_upload_buffer_init_pre(init_params);
+ if (!handle) {
+ return NULL;
+ }
+ handle->log_buffer_head_len = LOG_BUF_FIXED_HEADER_SIZE;
+ memset(handle->log_buffer, '#', handle->log_buffer_head_len);
+ handle->log_buffer[SIGNATURE_SIZE] = 'P';
+ memcpy(handle->log_buffer + SIGNATURE_SIZE + CTRL_BYTES_SIZE, init_params->product_id, MAX_SIZE_OF_PRODUCT_ID);
+ memcpy(handle->log_buffer + SIGNATURE_SIZE + CTRL_BYTES_SIZE + MAX_SIZE_OF_PRODUCT_ID, init_params->device_name,
+ strlen(init_params->device_name));
+ handle->write_index = handle->log_buffer_head_len;
+ handle->host = init_params->host;
+ _reset_log_buffer(handle);
+
+#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
+ HAL_Snprintf(sg_head_content, 128,
+ "Accept:application/json;*/*\r\n"
+ "X-TC-IotEncryType:%c\r\n"
+ "X-TC-IotCid:%s%s\r\n",
+ handle->log_buffer[SIGNATURE_SIZE], init_params->product_id, init_params->device_name);
+#endif
+
+ return handle;
+}
+
+#ifdef LOG_CHECK_HTTP_RET_CODE
+/**
+ * @brief Get http respose result.
+ *
+ * @param[in] json http response buffer
+ * @param[in] json_len http response buffer length
+ * @return true log report success
+ * @return false log report error
+ */
+static bool _get_json_ret_code(char *json, size_t json_len)
+{
+ int rc = 0;
+ uint32_t ret_code;
+ rc = utils_json_get_uint32("Retcode", strlen("Retcode"), json, json_len, &ret_code);
+ if (!rc) {
+ if (!ret_code) {
+ return true;
+ }
+ }
+ return false;
+}
+#endif
+
+/**
+ * @brief Update timestamp add signature post buffer.
+ *
+ * @param[in] handle @see LogUploadBuffer
+ * @param[in] post_buf need post data buffer
+ * @param[in] post_size need post data buffer length
+ */
+static void _update_time_and_signature(LogUploadBuffer *handle, char *post_buf, size_t post_size)
+{
+ char timestamp[TIMESTAMP_SIZE + 1] = {0};
+ char signature[SIGNATURE_SIZE + 1] = {0};
+ uint32_t now_time = IOT_Timer_CurrentSec();
+
+ /* record the timestamp for this log uploading */
+ HAL_Snprintf(timestamp, TIMESTAMP_SIZE + 1, "%010d", now_time);
+ memcpy(post_buf + sg_log_upload_buffer_handle->log_buffer_head_len - TIMESTAMP_SIZE, timestamp, strlen(timestamp));
+
+ /* signature of this log uploading */
+ utils_hmac_sha1_hex(post_buf + SIGNATURE_SIZE, post_size - SIGNATURE_SIZE, (uint8_t *)handle->sign_key,
+ strlen(handle->sign_key), signature);
+ memcpy(post_buf, signature, SIGNATURE_SIZE);
+}
+
+/**
+ * @brief Post data to tencent cloud over http.
+ *
+ * @param[in] post_buf need post data buffer
+ * @param[in] post_size need post data buffer length
+ * @return @see IotReturnCode
+ */
+int post_one_http_to_server(void *log_upload_buffer_handle, char *post_buf, size_t post_size)
+{
+ int rc = 0;
+#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
+ char *log_encrypt_buffer = NULL;
+#endif
+ LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
+ _update_time_and_signature(handle, post_buf, post_size);
+ void *http_client = IOT_HTTP_Init();
+ if (!http_client) {
+ UPLOAD_ERR("http client init error");
+ goto exit;
+ }
+ IotHTTPConnectParams connect_params = {
+ .url = handle->host ? handle->host : LOG_UPLOAD_OLD_SERVER_URL,
+ .port = LOG_UPLOAD_OLD_SERVER_PORT,
+ .ca_crt = NULL, // TODO: support cert
+ };
+
+ rc = IOT_HTTP_Connect(http_client, &connect_params);
+ if (rc) {
+ UPLOAD_ERR("http connect error.");
+ goto exit;
+ }
+ IotHTTPRequestParams request = {
+ .url = handle->host ? handle->host : LOG_UPLOAD_OLD_SERVER_URL,
+ .method = IOT_HTTP_METHOD_POST,
+ .content_length = post_size,
+ .content_type = "text/plain;charset=utf-8",
+ .content = post_buf,
+ .header = "Accept:application/json;*/*\r\n",
+ };
+
+#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
+ /*ANSI X.923: zero-padding with last byte as padlen */
+ int padlen = 16 - post_size % 16;
+ int datalen = post_size + padlen;
+ log_encrypt_buffer = HAL_Malloc(datalen + 1);
+ if (!log_encrypt_buffer) {
+ UPLOAD_ERR("malloc log encrypt buffer error. malloc length : %d", datalen);
+ rc = QCLOUD_ERR_MALLOC;
+ goto exit;
+ }
+ memset(log_encrypt_buffer, 0, datalen + 1);
+ memcpy(log_encrypt_buffer, post_buf, post_size);
+ log_encrypt_buffer[datalen - 1] = padlen;
+
+ // aes cbc
+ rc = _log_aes_cbc_encrypt(handle, (uint8_t *)log_encrypt_buffer, datalen);
+ if (QCLOUD_RET_SUCCESS != rc) {
+ UPLOAD_ERR("log encrypt failed, ret = %d", rc);
+ goto exit;
+ }
+ request.header = sg_head_content;
+ request.content_type = "application/octet-stream";
+ request.content_length = datalen;
+ request.content = log_encrypt_buffer;
+#endif
+
+ rc = IOT_HTTP_Request(http_client, &request);
+ // 2. check rc
+ if (rc) {
+ UPLOAD_ERR("http post error.");
+ goto exit;
+ }
+
+#ifdef LOG_CHECK_HTTP_RET_CODE
+ char buf[HTTP_RET_JSON_LENGTH] = {0};
+ rc = IOT_HTTP_Recv(http_client, (uint8_t *)buf, HTTP_RET_JSON_LENGTH, HTTP_WAIT_RET_TIMEOUT_MS);
+ if (rc < 0) {
+ UPLOAD_ERR("qcloud_http_recv_data failed, rc = %d", rc);
+ } else if (rc == 0) {
+ UPLOAD_ERR("HTTP Recv length == %d", rc);
+ rc = QCLOUD_ERR_HTTP_TIMEOUT;
+ } else {
+ buf[rc - 1] = '\0'; // json_parse relies on a string
+ if (strlen(buf) > 0 && _get_json_ret_code(buf, rc)) {
+ UPLOAD_DBG("Log server return SUCCESS: %s", buf);
+ rc = QCLOUD_RET_SUCCESS;
+ } else {
+ UPLOAD_ERR("Log server return FAIL: %s", buf);
+ rc = QCLOUD_ERR_HTTP;
+ }
+ }
+#endif
+
+exit:
+ if (http_client) {
+ IOT_HTTP_Disconnect(http_client);
+ IOT_HTTP_Deinit(http_client);
+ }
+#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
+ if (log_encrypt_buffer) {
+ HAL_Free(log_encrypt_buffer);
+ log_encrypt_buffer = NULL;
+ }
+#endif
+ UPLOAD_DBG("%ld data have be post to server. rc : %d", post_size, rc);
+ return rc;
+}
+
+/**
+ * @brief Push data to log buffer.
+ *
+ * @param[in] log_content need push data
+ * @return if 0 success else QCLOUD_ERR_FAILURE
+ */
+int log_upload_buffer_append(const char *log_content)
+{
+ uint16_t log_size = strlen(log_content);
+ if (HAL_MutexTryLock(sg_log_upload_buffer_handle->lock_buf) != 0) {
+ UPLOAD_ERR("trylock buffer failed!");
+ return QCLOUD_ERR_FAILURE;
+ }
+
+ if ((sg_log_upload_buffer_handle->write_index + log_size + 1) > sg_log_upload_buffer_handle->log_buffer_size) {
+ HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
+ UPLOAD_DBG("log upload buffer is not enough! remain : %d, need upload length: %d",
+ sg_log_upload_buffer_handle->log_buffer_size - sg_log_upload_buffer_handle->write_index, log_size);
+ return QCLOUD_ERR_BUF_TOO_SHORT;
+ }
+
+ memcpy(sg_log_upload_buffer_handle->log_buffer + sg_log_upload_buffer_handle->write_index, log_content, log_size);
+ sg_log_upload_buffer_handle->write_index += log_size;
+
+ /* replace \r\n to \n\f as delimiter */
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index - 1] = '\f';
+ sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index - 2] = '\n';
+
+ HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
+ return QCLOUD_RET_SUCCESS;
+}
+
+#endif
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/src/log_mqtt.c b/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/src/log_mqtt.c
new file mode 100644
index 00000000..9cd3795f
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/src/log_mqtt.c
@@ -0,0 +1,133 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file log_mqtt.c
+ * @brief
+ * @author hubertxxu (hubertxxu@tencent.com)
+ * @version 1.0
+ * @date 2022-01-05
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-01-05 1.0 hubertxxu first commit
+ *
+ */
+
+#include "log_upload.h"
+
+/**
+ * @brief Parse log level.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] message message from topic
+ * @param[in,out] usr_data pointer to @see LogLevelResultInfo
+ */
+static void _log_mqtt_message_callback(void *client, const MQTTMessage *message, void *usr_data)
+{
+ int rc;
+ uint32_t log_level;
+
+ Log_d("Receive log result message:%.*s", message->payload_len, message->payload_str);
+
+ // get log level
+ rc = utils_json_get_uint32("log_level", sizeof("log_level") - 1, message->payload_str, message->payload_len,
+ &log_level);
+ if (rc) {
+ return;
+ }
+
+ switch (log_level) {
+ case LOG_LEVEL_DISABLE:
+ log_upload_buffer_clear();
+ log_upload_set_upload_log_in_comm_err(true);
+ log_upload_set_log_upload_level(LOG_LEVEL_ERROR);
+ UPLOAD_DBG("Upload log level change to: %d", LOG_LEVEL_ERROR);
+ break;
+ case LOG_LEVEL_ERROR:
+ case LOG_LEVEL_WARN:
+ case LOG_LEVEL_INFO:
+ case LOG_LEVEL_DEBUG:
+ if (log_level < log_upload_get_log_upload_level()) {
+ log_upload_buffer_clear();
+ }
+ log_upload_set_upload_log_in_comm_err(false);
+ log_upload_set_log_upload_level((LogLevel)log_level);
+ UPLOAD_DBG("Upload log level change to: %d", log_level);
+ break;
+ default:
+ UPLOAD_ERR("Invalid log level: %d", log_level);
+ break;
+ }
+}
+
+/**
+ * @brief Check and subscribe log result topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return @see IotReturnCode
+ */
+static int _log_mqtt_result_topic_check_and_sub(void *client)
+{
+ char log_upload_topic[MAX_SIZE_OF_CLOUD_TOPIC];
+ /* subscribe the log topic: "$log/operation/result/${productId}/${deviceName}" */
+ HAL_Snprintf(log_upload_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$log/operation/result/%s/%s",
+ STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
+ STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
+ SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
+ sub_params.on_message_handler = _log_mqtt_message_callback;
+ sub_params.qos = QOS1;
+ return IOT_MQTT_SubscribeSync(client, log_upload_topic, &sub_params);
+}
+
+/**
+ * @brief Publish get log level message to log topic.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return @see IotReturnCode
+ */
+static int _log_mqtt_get_log_level_publish(void *client)
+{
+ static int sg_get_log_level_token_count = 0;
+ char topic_name[MAX_SIZE_OF_CLOUD_TOPIC];
+ char payload[128] = {0};
+
+ HAL_Snprintf(topic_name, sizeof(topic_name), "$log/operation/%s/%s",
+ STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
+ STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
+ HAL_Snprintf(payload, sizeof(payload), "{\"type\": \"get_log_level\",\"clientToken\": \"get_log_level-%u\"}",
+ sg_get_log_level_token_count++);
+
+ PublishParams pub_params = DEFAULT_PUB_PARAMS;
+ pub_params.qos = QOS0;
+ pub_params.payload = payload;
+ pub_params.payload_len = strlen(payload);
+ return IOT_MQTT_Publish(client, topic_name, &pub_params) >= 0 ? QCLOUD_RET_SUCCESS : QCLOUD_ERR_FAILURE;
+}
+
+/**
+ * @brief Init log upload module.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return @see IotReturnCode
+ */
+int log_mqtt_init(void *client)
+{
+ int rc = _log_mqtt_result_topic_check_and_sub(client);
+ if (rc) {
+ return rc;
+ }
+ return _log_mqtt_get_log_level_publish(client);
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/src/log_upload.c b/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/src/log_upload.c
new file mode 100644
index 00000000..e0dbd46d
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/src/log_upload.c
@@ -0,0 +1,551 @@
+/**
+ * @file log_upload.c
+ * @author {hubert} ({hubertxxu@tencent.com})
+ * @brief
+ * @version 1.0
+ * @date 2022-01-24
+ *
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2022-01-24 1.0 hubertxxu first commit
+ *
+ */
+
+#include "log_upload.h"
+
+#ifdef LOG_UPLOAD_TYPE_JSON
+#define LOG_DELIMITER "\", "
+#else
+#define LOG_DELIMITER "\n\f"
+#endif
+
+/**
+ * @brief Data structure for log upload.
+ *
+ */
+typedef struct {
+ bool upload_only_in_comm_err;
+ LogLevel log_level;
+ QcloudIotTimer upload_timer;
+ void *upload_buffer_handle;
+
+ const char *save_log_filename;
+ LogSaveFunc save_func;
+ LogReadFunc read_func;
+ LogDelFunc del_func;
+ LogGetSizeFunc get_size_func;
+
+ bool log_save_enabled;
+ bool log_client_init_done;
+} QcloudIoTLogUpload;
+
+static QcloudIoTLogUpload *sg_log_uploader_client;
+
+// ----------------------------------------------------------------------------
+// Internal API
+// ----------------------------------------------------------------------------
+
+/**
+ * @brief Get log upload client.
+ *
+ * @return @see QcloudIoTLogUpload
+ */
+static QcloudIoTLogUpload *_get_log_uploader_client(void)
+{
+ if (!sg_log_uploader_client) {
+ return NULL;
+ }
+ if (!sg_log_uploader_client->log_client_init_done) {
+ return NULL;
+ }
+ return sg_log_uploader_client;
+}
+
+/**
+ * @brief Set client.
+ *
+ * @param[in] client @see QcloudIoTLogUpload
+ */
+static void _set_log_uploader_client(void *client)
+{
+ sg_log_uploader_client = client;
+}
+
+/**
+ * @brief Set log upload in comm err state.
+ *
+ * @param state true or false
+ */
+void log_upload_set_upload_log_in_comm_err(bool state)
+{
+ QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
+ POINTER_SANITY_CHECK_RTN(log_uploader_handle);
+ log_uploader_handle->upload_only_in_comm_err = state;
+}
+
+/**
+ * @brief Change log upload level.
+ *
+ * @param[in] level @see LogLevel
+ */
+void log_upload_set_log_upload_level(LogLevel level)
+{
+ QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
+ POINTER_SANITY_CHECK_RTN(log_uploader_handle);
+ log_uploader_handle->log_level = level;
+}
+
+/**
+ * @brief Get log upload level.
+ *
+ * @return @see LogLevel
+ */
+LogLevel log_upload_get_log_upload_level(void)
+{
+ QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
+ POINTER_SANITY_CHECK(log_uploader_handle, LOG_LEVEL_DISABLE);
+ return log_uploader_handle->log_level;
+}
+
+/**
+ * @brief Check if need force upload.
+ *
+ * @param[in] force_upload
+ * @return true need force upload
+ * @return false not needed force upload
+ */
+static bool _check_force_upload(bool force_upload)
+{
+ QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
+ if (!log_uploader_handle) {
+ return false;
+ }
+ /* 1: check if have data to upload */
+ if (log_upload_buffer_is_empty(log_uploader_handle->upload_buffer_handle)) {
+ return false;
+ }
+
+ uint32_t write_index = log_upload_buffer_get_write_index(log_uploader_handle->upload_buffer_handle);
+ bool is_low_buffer = (log_upload_buffer_get_size() - write_index) < LOG_LOW_BUFFER_THRESHOLD ? true : false;
+
+ /* 2. check if upload only in comm err */
+ if (!force_upload && log_uploader_handle->upload_only_in_comm_err) {
+ /* buffer is low but we couldn't upload now, reset buffer */
+ if (is_low_buffer) {
+ log_upload_buffer_clear();
+ }
+ IOT_Timer_CountdownMs(&log_uploader_handle->upload_timer, LOG_UPLOAD_INTERVAL_MS);
+ return false;
+ }
+
+ /* 3: force upload is true, handle it right now */
+ if (force_upload) {
+ return true;
+ }
+ /* 4 : check if buffer is low */
+ if (is_low_buffer) {
+ /* buffer is low, handle it right now */
+ return true;
+ }
+ /*5: check if timeout, handle it right now*/
+ return IOT_Timer_Expired(&log_uploader_handle->upload_timer);
+}
+
+/**
+ * @brief Save log to file or flash.
+ *
+ * @param[in] log_buf need saved buffer
+ * @param[in] log_size need saved buffer length
+ * @return @see IotReturnCode
+ */
+static int _save_log(char *log_buf, size_t log_size)
+{
+ int rc = 0;
+ size_t write_size, current_size;
+ QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
+ POINTER_SANITY_CHECK(log_uploader_handle, QCLOUD_ERR_INVAL);
+ if (!log_size) {
+ UPLOAD_ERR("nothing need to save. log_size : %ld", log_size);
+ return -1;
+ }
+
+ current_size = log_uploader_handle->get_size_func(log_uploader_handle->save_log_filename);
+
+ /* overwrite the previous saved log to avoid too many saved logs */
+ if ((current_size + log_size) > MAX_LOG_SAVE_SIZE) {
+ UPLOAD_ERR("overwrite the previous saved log. %ld", current_size);
+ rc = log_uploader_handle->del_func(log_uploader_handle->save_log_filename);
+ if (rc) {
+ Log_e("fail to delete previous log");
+ }
+ current_size = 0;
+ }
+ UPLOAD_DBG("saved data [%ld]", log_size);
+ write_size =
+ log_uploader_handle->save_func(log_uploader_handle->save_log_filename, log_buf, log_size, current_size);
+ if (write_size != log_size) {
+ Log_e("fail to save log. RC %ld - log size %ld", write_size, log_size);
+ }
+
+ return write_size != log_size ? -1 : 0;
+}
+
+/**
+ * @brief Get at most message to upload.
+ *
+ * @param[in] post_buf post buffer
+ * @return size of message to upload
+ */
+static uint32_t _buffer_get_at_most_message(const char *post_buf)
+{
+ uint32_t possible_size = 0;
+ char *next_log_buf = NULL;
+ size_t delimiter_len = strlen(LOG_DELIMITER);
+ while (possible_size < MAX_HTTP_LOG_POST_SIZE) {
+ /* locate the delimiter */
+ next_log_buf = strstr(post_buf + possible_size, LOG_DELIMITER);
+ if (!next_log_buf) {
+ return 0;
+ }
+ possible_size = (size_t)(next_log_buf - post_buf + delimiter_len);
+ /* end of log */
+ if (next_log_buf[delimiter_len] == 0 && possible_size < MAX_HTTP_LOG_POST_SIZE) {
+ break;
+ }
+ }
+ return possible_size;
+}
+
+/**
+ * @brief Report log buffer to the server.
+ *
+ * @param[in,out] log_uploader_handle @see QcloudIoTLogUpload
+ * @param[in] post_buf post buffer
+ * @param[in] post_size post size
+ * @param[in] is_log_buffer log buffer or saved log
+ * @return @see IotReturnCode
+ */
+static int _post_log_buffer_to_cloud(QcloudIoTLogUpload *log_uploader_handle, char *post_buf, size_t post_size,
+ bool is_log_buffer)
+{
+ int rc = 0;
+ uint32_t log_buffer_head_len = log_upload_buffer_get_head_len(log_uploader_handle->upload_buffer_handle);
+
+ size_t orig_post_size = post_size;
+ size_t post_payload, possible_size, actual_post_payload = 0;
+ do {
+ // 1. get message in buffer
+ possible_size = _buffer_get_at_most_message(post_buf);
+ if (!possible_size) {
+ UPLOAD_ERR("Upload size should not be 0! Total sent: %ld. Left: %ld",
+ actual_post_payload + log_buffer_head_len, post_size);
+ if (is_log_buffer) {
+ log_upload_buffer_clear();
+ }
+ return QCLOUD_RET_SUCCESS;
+ }
+ if (is_log_buffer && (possible_size > log_upload_buffer_get_size() || possible_size > orig_post_size ||
+ possible_size <= log_buffer_head_len)) {
+ log_upload_buffer_clear();
+ UPLOAD_ERR("possible size (%ld) is too large.", possible_size);
+ return QCLOUD_RET_SUCCESS;
+ }
+ // 2. post to server
+ rc = post_one_http_to_server(log_uploader_handle->upload_buffer_handle, post_buf, possible_size);
+ if (rc) {
+ UPLOAD_ERR("Send log failed. Total sent: %ld. Left: %ld", actual_post_payload + log_buffer_head_len,
+ post_size);
+ // when send failed save log in file.
+ if (log_uploader_handle->log_save_enabled && is_log_buffer) {
+ _save_log(post_buf + log_buffer_head_len, possible_size - log_buffer_head_len);
+ }
+ }
+
+ // 3. remove have be post message
+ memmove(post_buf + log_buffer_head_len, post_buf + possible_size, post_size - possible_size);
+ post_payload = possible_size - log_buffer_head_len;
+ post_size -= post_payload;
+ actual_post_payload += post_payload;
+ memset(post_buf + post_size, 0, orig_post_size - post_size);
+ UPLOAD_DBG("post log %d OK. Total sent: %d. Left: %d ", possible_size,
+ actual_post_payload + log_buffer_head_len, post_size);
+ } while (post_size > log_buffer_head_len);
+
+ if (is_log_buffer) {
+ log_upload_buffer_remove_log(log_uploader_handle->upload_buffer_handle, orig_post_size);
+ }
+
+ return rc;
+}
+
+/**
+ * @brief Handle log to save flash or file.
+ *
+ * @return @see IotReturnCode
+ */
+static int _handle_saved_log(void)
+{
+ int rc = QCLOUD_RET_SUCCESS;
+ QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
+ if (!log_uploader_handle) {
+ return QCLOUD_ERR_FAILURE;
+ }
+
+ size_t whole_log_size = log_uploader_handle->get_size_func(log_uploader_handle->save_log_filename);
+ if (!whole_log_size) {
+ return rc;
+ }
+ uint32_t log_buffer_head_len = log_upload_buffer_get_head_len(log_uploader_handle->upload_buffer_handle);
+ size_t buf_size = whole_log_size + log_buffer_head_len + 1;
+ char *log_buf = HAL_Malloc(buf_size);
+ if (!log_buf) {
+ UPLOAD_ERR("Malloc failed, size: %ld", buf_size);
+ return QCLOUD_ERR_FAILURE;
+ }
+
+ /* read the whole log to buffer */
+ size_t read_len = log_uploader_handle->read_func(log_uploader_handle->save_log_filename,
+ log_buf + log_buffer_head_len, whole_log_size, 0);
+ if (read_len != whole_log_size) {
+ Log_e("fail to read whole saved log. Size: %ld - read: %ld", whole_log_size, read_len);
+ rc = QCLOUD_ERR_FAILURE;
+ goto exit;
+ }
+
+ /* copy header from global log buffer */
+ size_t upload_size = whole_log_size + log_buffer_head_len;
+ log_upload_buffer_copy_header(log_buf, log_uploader_handle->upload_buffer_handle);
+ log_buf[buf_size - 1] = 0;
+
+ /* post server */
+ rc = _post_log_buffer_to_cloud(log_uploader_handle, log_buf, upload_size, false);
+ if (rc == QCLOUD_RET_SUCCESS || rc == QCLOUD_ERR_INVAL || rc == QCLOUD_ERR_HTTP) {
+ UPLOAD_DBG("handle saved log done! Size: %ld. ", whole_log_size);
+ log_uploader_handle->del_func(log_uploader_handle->save_log_filename);
+ }
+exit:
+ HAL_Free(log_buf);
+ return rc;
+}
+
+/**
+ * @brief Append need report log to log upload buffer.
+ *
+ * @param[in] log_level @see LogLevel
+ * @param[in] log_content data of need to report
+ */
+static void _log_upload_append_to_log_buffer(LogLevel log_level, const char *log_content)
+{
+ int rc = 0;
+ QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
+ // POINTER_SANITY_CHECK_RTN(log_uploader_handle);
+ if (!log_uploader_handle) {
+ return;
+ }
+ // if not necessary. just give up upload.
+ if (log_level > log_uploader_handle->log_level) {
+ return;
+ }
+
+ uint16_t log_size = strlen(log_content);
+ if (!log_size) {
+ UPLOAD_ERR("log_size is not 0!");
+ return;
+ }
+ rc = log_upload_buffer_append(log_content);
+ if (rc) {
+ /* log buffer is full. upload data right now */
+ IOT_Timer_CountdownMs(&log_uploader_handle->upload_timer, 0);
+ }
+}
+
+/**
+ * @brief Upload data to tencent cloud.
+ *
+ * @param[in] force_upload upload if necessary
+ * @return @see IotReturnCode
+ */
+static int _do_log_upload(bool force_upload)
+{
+ int rc;
+ static bool sg_need_handle_saved_log = true;
+
+ QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
+ POINTER_SANITY_CHECK(log_uploader_handle, QCLOUD_ERR_INVAL);
+
+ /* step1: handle previously saved log */
+ if (log_uploader_handle->log_save_enabled && sg_need_handle_saved_log) {
+ rc = _handle_saved_log();
+ if (!rc) {
+ sg_need_handle_saved_log = false;
+ }
+ }
+
+ /* step2: check if have data to upload */
+ if (!_check_force_upload(force_upload)) {
+ return QCLOUD_RET_SUCCESS;
+ }
+
+ // log_buffer_lock();
+
+ /* step3: post log buffer data to cloud */
+ uint32_t upload_log_size = log_upload_buffer_get_write_index(log_uploader_handle->upload_buffer_handle);
+
+ rc = _post_log_buffer_to_cloud(
+ log_uploader_handle, log_upload_buffer_get(log_uploader_handle->upload_buffer_handle), upload_log_size, true);
+ if (rc) {
+ sg_need_handle_saved_log = true;
+ }
+
+ IOT_Timer_CountdownMs(&log_uploader_handle->upload_timer, LOG_UPLOAD_INTERVAL_MS);
+
+ // log_buffer_unlock();
+ return QCLOUD_RET_SUCCESS;
+}
+
+/**
+ * @brief Init log upload previously.
+ *
+ * @param[in] init_params @see LogUploadInitParams
+ * @return @see IotReturnCode
+ */
+static int log_upload_init_pre(const LogUploadInitParams *init_params)
+{
+ if (_get_log_uploader_client()) {
+ UPLOAD_DBG("log upload initialized.");
+ return QCLOUD_RET_SUCCESS;
+ }
+
+ QcloudIoTLogUpload *log_uploader_handle = HAL_Malloc(sizeof(QcloudIoTLogUpload));
+ if (!log_uploader_handle) {
+ UPLOAD_ERR("allocate for log client failed");
+ goto err_exit;
+ }
+
+ log_uploader_handle->upload_only_in_comm_err = false;
+
+ /* all the call back functions are necessary to handle log save and re-upload*/
+ if (init_params->save_func && init_params->read_func && init_params->del_func && init_params->get_size_func &&
+ init_params->save_log_filename) {
+ log_uploader_handle->save_log_filename = init_params->save_log_filename;
+ log_uploader_handle->save_func = init_params->save_func;
+ log_uploader_handle->read_func = init_params->read_func;
+ log_uploader_handle->del_func = init_params->del_func;
+ log_uploader_handle->get_size_func = init_params->get_size_func;
+ log_uploader_handle->log_save_enabled = true;
+ } else {
+ log_uploader_handle->log_save_enabled = false;
+ }
+
+ log_uploader_handle->upload_buffer_handle = log_upload_buffer_init(init_params);
+ if (!log_uploader_handle->upload_buffer_handle) {
+ goto err_exit;
+ }
+
+ _set_log_uploader_client(log_uploader_handle);
+ log_uploader_handle->log_level = LOG_LEVEL_ERROR;
+ log_uploader_handle->log_client_init_done = true;
+ return QCLOUD_RET_SUCCESS;
+
+err_exit:
+ if (log_uploader_handle) {
+ log_upload_buffer_deinit(log_uploader_handle->upload_buffer_handle);
+ HAL_Free(log_uploader_handle);
+ log_uploader_handle = NULL;
+ }
+ return QCLOUD_ERR_FAILURE;
+}
+
+/**
+ * @brief Stop log upload add release resources.
+ *
+ * @return @see IotReturnCode
+ */
+static int log_upload_deinit(void)
+{
+ QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
+ POINTER_SANITY_CHECK(log_uploader_handle, QCLOUD_ERR_INVAL);
+ log_upload_buffer_deinit(log_uploader_handle->upload_buffer_handle);
+ HAL_Free(log_uploader_handle);
+ log_uploader_handle = NULL;
+ return QCLOUD_RET_SUCCESS;
+}
+
+// ----------------------------------------------------------------------------
+// External API
+// ----------------------------------------------------------------------------
+
+/**
+ * @brief Init log upload previously.
+ *
+ * @param[in] init_params @see LogUploadInitParams
+ * @return @see IotReturnCode
+ */
+int IOT_Log_Upload_InitPre(const LogUploadInitParams *init_params)
+{
+ POINTER_SANITY_CHECK(init_params, QCLOUD_ERR_INVAL);
+ STRING_PTR_SANITY_CHECK(init_params->product_id, QCLOUD_ERR_INVAL);
+ STRING_PTR_SANITY_CHECK(init_params->device_name, QCLOUD_ERR_INVAL);
+ STRING_PTR_SANITY_CHECK(init_params->sign_key, QCLOUD_ERR_INVAL);
+ return log_upload_init_pre(init_params);
+}
+
+/**
+ * @brief Init log upload module.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return @see IotReturnCode
+ */
+int IOT_Log_Upload_Init(void *client)
+{
+ POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
+ return log_mqtt_init(client);
+}
+
+/**
+ * @brief Stop log upload add release resources.
+ *
+ * @return @see IotReturnCode
+ */
+int IOT_Log_Upload_Deinit(void)
+{
+ return log_upload_deinit();
+}
+
+/**
+ * @brief Do log upload.
+ *
+ * @param[in] force_upload force upload when error
+ * @return @see IotReturnCode
+ */
+int IOT_Log_Upload(bool force_upload)
+{
+ return _do_log_upload(force_upload);
+}
+
+/**
+ * @brief Append need report log to log upload buffer.
+ *
+ * @param[in] log_level @see LogLevel
+ * @param[in] log_content data of need to report
+ */
+void IOT_Log_Upload_AppendToUploadBuffer(LogLevel log_level, const char *log_content)
+{
+ POINTER_SANITY_CHECK_RTN(log_content);
+ _log_upload_append_to_log_buffer(log_level, log_content);
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/test/.clang-format b/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/test/.clang-format
new file mode 100644
index 00000000..334c1c0d
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/test/.clang-format
@@ -0,0 +1,8 @@
+Language: Cpp
+BasedOnStyle: Google
+ColumnLimit: 120
+DerivePointerAlignment: true
+PointerAlignment: Left
+SortIncludes: true
+IncludeBlocks: Preserve
+IndentPPDirectives: AfterHash
\ No newline at end of file
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/test/test_log_upload.cc b/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/test/test_log_upload.cc
new file mode 100644
index 00000000..b132fd7e
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/log_upload/test/test_log_upload.cc
@@ -0,0 +1,79 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file test_broadcast.cc
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-07-18
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-07-18 1.0 fancyxu first commit
+ *
+ */
+
+#include
+#include
+
+#include "gtest/gtest.h"
+#include "mqtt_client_test.h"
+#include "qcloud_iot_common.h"
+
+namespace mqtt_client_unittest {
+
+/**
+ * @brief Test log upload
+ *
+ */
+TEST_F(MqttClientTest, log_upload_test) {
+ LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
+ func.log_upload = IOT_Log_Upload_AppendToUploadBuffer;
+ utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
+
+ LogUploadInitParams log_upload_init_params = DEFAULT_LOG_UPLOAD_INIT_PARAMS;
+ log_upload_init_params.device_name = device_info.device_name;
+ log_upload_init_params.product_id = device_info.product_id;
+ log_upload_init_params.sign_key = device_info.device_secret;
+
+ log_upload_init_params.log_buffer_size = LOG_UPLOAD_BUFFER_SIZE;
+ log_upload_init_params.save_log_filename = "./tmp/upload-fail-save.log";
+ log_upload_init_params.read_func = HAL_File_Read;
+ log_upload_init_params.save_func = HAL_File_Write;
+ log_upload_init_params.del_func = HAL_File_Del;
+ log_upload_init_params.get_size_func = HAL_File_GetSize;
+
+ ASSERT_EQ(IOT_Log_Upload_InitPre(&log_upload_init_params), 0);
+ ASSERT_EQ(IOT_Log_Upload_Init(client), 0);
+
+ uint32_t loop_cnt = 10;
+ int rc = 0;
+ do {
+ Log_d("log upload test debug %d...", loop_cnt);
+ Log_i("log upload test info %d...", loop_cnt);
+ Log_w("log upload test waring %d...", loop_cnt);
+ Log_e("log upload test error %d...", loop_cnt);
+ rc = IOT_MQTT_Yield(client, 200);
+ ASSERT_EQ(IOT_Log_Upload(rc ? true : false), 0);
+ } while (loop_cnt--);
+
+ ASSERT_EQ(IOT_Log_Upload_Deinit(), 0);
+
+ func.log_upload = NULL;
+ utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
+}
+
+} // namespace mqtt_client_unittest
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/mqtt_client/CMakeLists.txt b/components/connectivity/iot-hub-device-c-sdk/services/common/mqtt_client/CMakeLists.txt
new file mode 100644
index 00000000..4f5bcfa9
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/mqtt_client/CMakeLists.txt
@@ -0,0 +1,18 @@
+file(GLOB src_mqtt_client ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
+set(inc_mqtt_client ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
+
+set(src_services ${src_services} ${src_mqtt_client} PARENT_SCOPE)
+set(inc_services ${inc_services} ${inc_mqtt_client} PARENT_SCOPE)
+
+if( ${CONFIG_COMPILE_SAMPLE} STREQUAL "ON")
+ file(GLOB src_mqtt_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/mqtt_sample.c)
+ add_executable(mqtt_sample ${src_mqtt_sample})
+ target_link_libraries(mqtt_sample ${libsdk})
+endif()
+
+if( ${CONFIG_IOT_TEST} STREQUAL "ON")
+ file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
+ set(inc_mqtt_client_test ${CMAKE_CURRENT_SOURCE_DIR}/test)
+ set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
+ set(inc_test ${inc_test} ${inc_mqtt_client_test} PARENT_SCOPE)
+endif()
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/mqtt_client/inc/mqtt_client.h b/components/connectivity/iot-hub-device-c-sdk/services/common/mqtt_client/inc/mqtt_client.h
new file mode 100644
index 00000000..43e540d0
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/mqtt_client/inc/mqtt_client.h
@@ -0,0 +1,446 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file mqtt_client.h
+ * @brief mqtt client internel api
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-31
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-31 1.0 fancyxu first commit
+ * 2021-07-08 1.1 fancyxu fix code standard of IotReturnCode and QcloudIotClient
+ *
+ */
+
+#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_MQTT_CLIENT_INC_MQTT_CLIENT_H_
+#define IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_MQTT_CLIENT_INC_MQTT_CLIENT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "qcloud_iot_mqtt_client.h"
+
+#include "mqtt_packet.h"
+
+#include "network_interface.h"
+
+#include "utils_list.h"
+#include "utils_base64.h"
+#include "utils_hmac.h"
+
+/**
+ * @brief Packet id, random from [1 - 65536]
+ *
+ */
+#define MAX_PACKET_ID (65535)
+
+/**
+ * @brief Max size of conn Id
+ *
+ */
+#define MAX_CONN_ID_LEN (6)
+
+/**
+ * @brief Max number of topic subscribed
+ *
+ */
+#define MAX_MESSAGE_HANDLERS (10)
+
+/**
+ * @brief Max number in repub list
+ *
+ */
+#define MAX_REPUB_NUM (20)
+
+/**
+ * @brief Minimal wait interval when reconnect
+ *
+ */
+#define MIN_RECONNECT_WAIT_INTERVAL (1000)
+
+/**
+ * @brief Minimal MQTT timeout value
+ *
+ */
+#define MIN_COMMAND_TIMEOUT (500)
+
+/**
+ * @brief Maxmal MQTT timeout value
+ *
+ */
+#define MAX_COMMAND_TIMEOUT (20000)
+
+/**
+ * @brief Minimal TLS handshaking timeout value (unit: ms)
+ *
+ */
+#define QCLOUD_IOT_TLS_HANDSHAKE_TIMEOUT (5 * 1000)
+
+/**
+ * @brief Enable repeat packet id filter
+ *
+ */
+#define MQTT_RMDUP_MSG_ENABLED
+
+/**
+ * @brief Connect status of mqtt server
+ *
+ */
+typedef enum {
+ NOTCONNECTED = 0,
+ CONNECTED = 1,
+} ConnStatus;
+
+/**
+ * @brief mqtt connection mode
+ *
+ */
+typedef enum {
+ FIRST_CONNECT = 0,
+ RECONNECT = 1,
+} ConnMode;
+
+/*
+ * @brief Subscribe status of topic.
+ *
+ */
+typedef enum {
+ SUB_ACK_NOT_RECEIVED = 0,
+ SUB_ACK_RECEIVED = 1,
+} SubStatus;
+
+/**
+ * @brief data structure for topic subscription handle
+ */
+typedef struct {
+ char *topic_filter; /**< topic name, wildcard filter is supported */
+ SubStatus status; /**< status of sub handle */
+ SubscribeParams params; /**< params needed to subscribe */
+} SubTopicHandle;
+
+/**
+ * @brief MQTT QCloud IoT Client structure
+ *
+ */
+typedef struct {
+ DeviceInfo *device_info; /**< device info needed to connect server */
+
+ uint16_t next_packet_id; /**< MQTT random packet id */
+ uint32_t command_timeout_ms; /**< MQTT command timeout, unit:ms */
+
+ uint8_t write_buf[QCLOUD_IOT_MQTT_TX_BUF_LEN]; /**< MQTT write buffer */
+ uint8_t read_buf[QCLOUD_IOT_MQTT_RX_BUF_LEN]; /**< MQTT read buffer */
+ size_t write_buf_size; /**< size of MQTT write buffer */
+ size_t read_buf_size; /**< size of MQTT read buffer */
+
+ MQTTEventHandler event_handle; /**< callback for MQTT event */
+ uint8_t auto_connect_enable; /**< enable auto connection or not */
+ uint8_t default_subscribe; /**< no subscribe packet send, only add subhandle */
+
+ void *lock_generic; /**< mutex/lock for this client struture */
+ void *lock_write_buf; /**< mutex/lock for write buffer */
+ void *list_pub_wait_ack; /**< puback waiting list */
+ void *list_sub_wait_ack; /**< suback waiting list */
+
+ char host_addr[HOST_STR_LENGTH]; /**< MQTT server host */
+ MQTTGetNextHostIp get_next_host_ip; // get host ip
+ const char *main_host;
+ const char *backup_host; // if host not connect will try this.
+ IotNetwork network_stack; /**< MQTT network stack */
+
+ MQTTPacketConnectOption options; /**< handle to connection parameters */
+ char conn_id[MAX_CONN_ID_LEN]; /**< connect id */
+
+ SubTopicHandle sub_handles[MAX_MESSAGE_HANDLERS]; /**< subscription handle array */
+ QcloudIotTimer ping_timer; /**< MQTT ping timer */
+ QcloudIotTimer reconnect_delay_timer; /**< MQTT reconnect delay timer */
+ uint8_t was_manually_disconnected; /**< was disconnect by server or device */
+ uint8_t is_ping_outstanding; /**< 1 = ping request is sent while ping response not arrived yet */
+ uint32_t current_reconnect_wait_interval; /**< unit:ms */
+
+ uint8_t is_connected; /**< is connected or not */
+ uint32_t counter_network_disconnected; /**< number of disconnection*/
+
+#ifdef MQTT_RMDUP_MSG_ENABLED
+#define MQTT_MAX_REPEAT_BUF_LEN 10
+ uint16_t repeat_packet_id_buf[MQTT_MAX_REPEAT_BUF_LEN]; /**< repeat packet id buffer */
+ unsigned int current_packet_id_cnt; /**< index of packet id buffer */
+#endif
+} QcloudIotClient;
+
+/**
+ * @brief topic publish info
+ *
+ */
+typedef struct {
+ uint8_t *buf; /**< msg buffer */
+ uint32_t len; /**< msg length */
+ uint16_t packet_id; /**< packet id */
+ QcloudIotTimer pub_start_time; /**< timer for puback waiting */
+} QcloudIotPubInfo;
+
+/**
+ * @brief topic subscribe/unsubscribe info
+ *
+ */
+typedef struct {
+ uint8_t *buf; /**< msg buffer */
+ uint16_t len; /**< msg length */
+ MQTTPacketType type; /**< type: sub or unsub */
+ uint16_t packet_id; /**< packet id */
+ QcloudIotTimer sub_start_time; /**< timer for suback waiting */
+ SubTopicHandle handler; /**< handle of topic subscribed(unsubscribed) */
+} QcloudIotSubInfo;
+
+/**************************************************************************************
+ * common
+ **************************************************************************************/
+
+/**
+ * @brief Get the next packet id object.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return packet id
+ */
+uint16_t get_next_packet_id(QcloudIotClient *client);
+
+/**
+ * @brief Get the next conn id object.
+ *
+ * @param[out] conn_id buffer to save conn id
+ */
+void get_next_conn_id(char *conn_id);
+
+/**
+ * @brief Set the client conn state object.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] connected connect status, @see ConnStatus
+ */
+void set_client_conn_state(QcloudIotClient *client, uint8_t connected);
+
+/**
+ * @brief Get the client conn state object.
+ *
+ * @param[in,out] client
+ * @return @see ConnStatus
+ */
+uint8_t get_client_conn_state(QcloudIotClient *client);
+
+/**
+ * @brief Send mqtt packet, timeout = command_timeout_ms.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] length length of data to be sent, data is saved in client write_buf
+ * @return @see IotReturnCode
+ */
+int send_mqtt_packet(QcloudIotClient *client, size_t length);
+
+/**************************************************************************************
+ * connect
+ **************************************************************************************/
+
+/**
+ * @brief Connect MQTT server.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] mode when connect error. Choose different connection strategies according to the mode
+ * @return @see IotReturnCode
+ */
+int qcloud_iot_mqtt_connect(QcloudIotClient *client, ConnMode mode);
+
+/**
+ * @brief Reconnect MQTT server.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return @see IotReturnCode
+ */
+int qcloud_iot_mqtt_attempt_reconnect(QcloudIotClient *client);
+
+/**
+ * @brief Disconnect MQTT server.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return @see IotReturnCode
+ */
+int qcloud_iot_mqtt_disconnect(QcloudIotClient *client);
+
+/**
+ * @brief Serialize and send pingreq packet.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] try_times if failed, retry times
+ * @return @see IotReturnCode
+ */
+int qcloud_iot_mqtt_pingreq(QcloudIotClient *client, int try_times);
+
+/**************************************************************************************
+ * publish
+ **************************************************************************************/
+
+/**
+ * @brief Serialize and send publish packet.
+ *
+ * @param[in,out] client pointer to mqtt_client
+ * @param[in] topic_name topic to publish
+ * @param[in] params publish params
+ * @return >=0 for packet id, < 0 for failed @see IotReturnCode
+ */
+int qcloud_iot_mqtt_publish(QcloudIotClient *client, const char *topic_name, const PublishParams *params);
+
+/**
+ * @brief Deserialize publish packet and deliver_message.
+ *
+ * @param[in,out] client pointer to mqtt_client
+ * @param[in] params publish params
+ * @return >=0 for packet id, < 0 for failed @see IotReturnCode
+ */
+int qcloud_iot_mqtt_handle_publish(QcloudIotClient *client);
+
+/**
+ * @brief Deserialize puback packet.
+ *
+ * @param[in,out] client pointer to mqtt_client
+ * @return 0 for success.
+ */
+int qcloud_iot_mqtt_handle_puback(QcloudIotClient *client);
+
+/**
+ * @brief Process puback waiting timout.
+ *
+ * @param[in,out] client pointer to mqtt_client
+ */
+void qcloud_iot_mqtt_check_pub_timeout(QcloudIotClient *client);
+
+/**************************************************************************************
+ * subscribe
+ **************************************************************************************/
+
+/**
+ * @brief Serialize and send subscribe packet.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic to subscribe
+ * @param[in] params subscribe params
+ * @return >=0 for packet id, < 0 for failed @see IotReturnCode
+ */
+int qcloud_iot_mqtt_subscribe(QcloudIotClient *client, const char *topic_filter, const SubscribeParams *params);
+
+/**
+ * @brief Deserialize suback packet and return sub result.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return @see IotReturnCode
+ */
+int qcloud_iot_mqtt_handle_suback(QcloudIotClient *client);
+
+/**
+ * @brief Serialize and send unsubscribe packet.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic to unsubscribe
+ * @return >=0 packet id, < 0 for failed @see IotReturnCode
+ */
+int qcloud_iot_mqtt_unsubscribe(QcloudIotClient *client, const char *topic_filter);
+
+/**
+ * @brief Deserialize unsuback packet and remove from list.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return @see IotReturnCode
+ */
+int qcloud_iot_mqtt_handle_unsuback(QcloudIotClient *client);
+
+/**
+ * @brief Process suback waiting timeout.
+ *
+ * @param[in,out] client pointer to mqtt client
+ */
+void qcloud_iot_mqtt_check_sub_timeout(QcloudIotClient *client);
+
+/**
+ * @brief Resubscribe topic when reconnect.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @return @see IotReturnCode
+ */
+int qcloud_iot_mqtt_resubscribe(QcloudIotClient *client);
+
+/**
+ * @brief Return if topic is sub ready.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter
+ * @return true for ready
+ * @return false for not ready
+ */
+bool qcloud_iot_mqtt_is_sub_ready(QcloudIotClient *client, const char *topic_filter);
+
+/**
+ * @brief Get usr data, usr should handle lock/unlock usrdata itself in callback and caller.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] topic_filter topic filter
+ * @return NULL or user data
+ */
+void *qcloud_iot_mqtt_get_subscribe_usr_data(QcloudIotClient *client, const char *topic_filter);
+
+/**
+ * @brief Clear sub handle array.
+ *
+ * @param[in,out] client pointer to mqtt client
+ */
+void qcloud_iot_mqtt_sub_handle_array_clear(QcloudIotClient *client);
+
+/**
+ * @brief Clear suback wait list and clear sub handle.
+ *
+ * @param[in,out] client pointer to mqtt client
+ */
+void qcloud_iot_mqtt_suback_wait_list_clear(QcloudIotClient *client);
+
+/**************************************************************************************
+ * yield
+ **************************************************************************************/
+
+/**
+ * @brief Check connection and keep alive state, read/handle MQTT message in synchronized way.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] timeout_ms timeout value (unit: ms) for this operation
+ *
+ * @return QCLOUD_RET_SUCCESS when success, QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT when try reconnecting, others @see
+ * IotReturnCode
+ */
+int qcloud_iot_mqtt_yield(QcloudIotClient *client, uint32_t timeout_ms);
+
+/**
+ * @brief Wait read specific mqtt packet, such as connack.
+ *
+ * @param[in,out] client pointer to mqtt client
+ * @param[in] packet_type packet type except to read
+ * @return @see IotReturnCode
+ */
+int qcloud_iot_mqtt_wait_for_read(QcloudIotClient *client, uint8_t packet_type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_MQTT_CLIENT_INC_MQTT_CLIENT_H_
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/mqtt_client/sample/mqtt_sample.c b/components/connectivity/iot-hub-device-c-sdk/services/common/mqtt_client/sample/mqtt_sample.c
new file mode 100644
index 00000000..cd18d8ee
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/mqtt_client/sample/mqtt_sample.c
@@ -0,0 +1,297 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file mqtt_sample.c
+ * @brief a simple sample for mqtt client
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-31
+ *
+ * @par Change Log:
+ *
+ * Date Version Author Description
+ * 2021-05-31 1.0 fancyxu first commit
+ * 2021-07-08 1.1 fancyxu fix code standard of IotReturnCode and QcloudIotClient
+ *
+ */
+
+#include
+#include
+#include
+#include
+
+#include "qcloud_iot_common.h"
+
+#include "utils_log.h"
+
+/**
+ * @brief MQTT event callback, @see MQTTEventHandleFun
+ *
+ * @param[in] client pointer to mqtt client
+ * @param[in] handle_context context
+ * @param[in] msg msg
+ */
+static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
+{
+ MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
+ uintptr_t packet_id = (uintptr_t)msg->msg;
+
+ switch (msg->event_type) {
+ case MQTT_EVENT_UNDEF:
+ Log_i("undefined event occur.");
+ break;
+
+ case MQTT_EVENT_DISCONNECT:
+ Log_i("MQTT disconnect.");
+ break;
+
+ case MQTT_EVENT_RECONNECT:
+ Log_i("MQTT reconnect.");
+ break;
+
+ case MQTT_EVENT_PUBLISH_RECEIVED:
+ Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
+ mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
+ mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
+ break;
+ case MQTT_EVENT_SUBSCRIBE_SUCCESS:
+ Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
+ Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_SUBSCRIBE_NACK:
+ Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
+ Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
+ Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_UNSUBSCRIBE_NACK:
+ Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_PUBLISH_SUCCESS:
+ Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_PUBLISH_TIMEOUT:
+ Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
+ break;
+
+ case MQTT_EVENT_PUBLISH_NACK:
+ Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
+ break;
+ default:
+ Log_i("Should NOT arrive here.");
+ break;
+ }
+}
+
+/**
+ * @brief Setup MQTT construct parameters.
+ *
+ * @param[in,out] initParams @see MQTTInitParams
+ * @param[in] device_info @see DeviceInfo
+ */
+static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
+{
+ init_params->device_info = device_info;
+ init_params->event_handle.h_fp = _mqtt_event_handler;
+}
+
+/**
+ * @brief Publish a test mqtt message.
+ *
+ * @param[in, out] client pointer to mqtt client
+ * @param[in] topic_keyword topic keyword
+ * @param[in] qos qos to publish
+ * @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
+ */
+static int _publish_test_msg(void *client, const char *topic_keyword, QoS qos)
+{
+ char topic_name[MAX_SIZE_OF_CLOUD_TOPIC] = {0};
+
+ DeviceInfo *dev_info = IOT_MQTT_GetDeviceInfo(client);
+
+ HAL_Snprintf(topic_name, sizeof(topic_name), "%s/%s/%s", STRING_PTR_PRINT_SANITY_CHECK(dev_info->product_id),
+ STRING_PTR_PRINT_SANITY_CHECK(dev_info->device_name), STRING_PTR_PRINT_SANITY_CHECK(topic_keyword));
+
+ static int test_count = 0;
+ PublishParams pub_params = DEFAULT_PUB_PARAMS;
+ pub_params.qos = qos;
+
+ char publish_content[128] = {0};
+ HAL_Snprintf(publish_content, sizeof(publish_content), "{\"action\": \"publish_test\", \"count\": \"%d\"}",
+ test_count++);
+ pub_params.payload = publish_content;
+ pub_params.payload_len = strlen(publish_content);
+ return IOT_MQTT_Publish(client, topic_name, &pub_params);
+}
+
+/**
+ * @brief Callback when MQTT msg arrives @see OnMessageHandler
+ *
+ * @param[in, out] client pointer to mqtt client
+ * @param[in] message publish message from server
+ * @param[in] usr_data user data of SubscribeParams, @see SubscribeParams
+ */
+static void _on_message_callback(void *client, const MQTTMessage *message, void *user_data)
+{
+ if (!message) {
+ return;
+ }
+
+ Log_i("Receive Message With topicName:%.*s, payload:%.*s", message->topic_len,
+ STRING_PTR_PRINT_SANITY_CHECK(message->topic_name), message->payload_len,
+ STRING_PTR_PRINT_SANITY_CHECK(message->payload_str));
+}
+
+/**
+ * @brief Subscribe MQTT topic and wait for sub result.
+ *
+ * @param[in, out] client pointer to mqtt client
+ * @param[in] topic_keyword topic keyword
+ * @param[in] qos qos to subscribe
+ * @return @see IotReturnCode
+ */
+static int _subscribe_topic_wait_result(void *client, char *topic_keyword, QoS qos)
+{
+ char topic_name[MAX_SIZE_OF_CLOUD_TOPIC] = {0};
+
+ DeviceInfo *dev_info = IOT_MQTT_GetDeviceInfo(client);
+
+ HAL_Snprintf(topic_name, sizeof(topic_name), "%s/%s/%s", STRING_PTR_PRINT_SANITY_CHECK(dev_info->product_id),
+ STRING_PTR_PRINT_SANITY_CHECK(dev_info->device_name), STRING_PTR_PRINT_SANITY_CHECK(topic_keyword));
+
+ SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
+ sub_params.qos = qos;
+ sub_params.on_message_handler = _on_message_callback;
+
+ return IOT_MQTT_SubscribeSync(client, topic_name, &sub_params);
+}
+
+/**
+ * @brief Unsubscribe MQTT topic.
+ *
+ * @param[in, out] client pointer to mqtt client
+ * @param[in] topic_keyword topic keyword
+ * @return @see IotReturnCode
+ */
+static int _unsubscribe_topic(void *client, char *topic_keyword)
+{
+ char topic_name[MAX_SIZE_OF_CLOUD_TOPIC] = {0};
+
+ DeviceInfo *dev_info = IOT_MQTT_GetDeviceInfo(client);
+
+ HAL_Snprintf(topic_name, sizeof(topic_name), "%s/%s/%s", STRING_PTR_PRINT_SANITY_CHECK(dev_info->product_id),
+ STRING_PTR_PRINT_SANITY_CHECK(dev_info->device_name), STRING_PTR_PRINT_SANITY_CHECK(topic_keyword));
+
+ int rc = IOT_MQTT_Unsubscribe(client, topic_name);
+ if (rc < 0) {
+ Log_e("MQTT unsubscribe FAILED: %d", rc);
+ return rc;
+ }
+
+ return IOT_MQTT_Yield(client, 500); // wait for unsuback
+}
+
+// ----------------------------------------------------------------------------
+// Main
+// ----------------------------------------------------------------------------
+static int sg_main_exit = 0;
+
+#ifdef __linux__
+
+#include
+#include
+#include
+
+static void _main_exit(int sig)
+{
+ Log_e("demo exit by signal:%d\n", sig);
+ sg_main_exit = 1;
+}
+#endif
+
+int main(int argc, char **argv)
+{
+#ifdef __linux__
+ signal(SIGINT, _main_exit);
+#endif
+
+ int rc;
+
+ // init log level
+ LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
+ utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
+
+ DeviceInfo device_info;
+
+ rc = HAL_GetDevInfo((void *)&device_info);
+ if (rc) {
+ Log_e("get device info failed: %d", rc);
+ return rc;
+ }
+
+ // init connection
+ MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
+ _setup_connect_init_params(&init_params, &device_info);
+
+ // create MQTT client and connect with server
+ void *client = IOT_MQTT_Construct(&init_params);
+ if (client) {
+ Log_i("Cloud Device Construct Success");
+ } else {
+ Log_e("MQTT Construct failed!");
+ return QCLOUD_ERR_FAILURE;
+ }
+
+ // subscribe normal topics and wait result
+ rc = _subscribe_topic_wait_result(client, "data", QOS0);
+ if (rc) {
+ Log_e("Client Subscribe Topic Failed: %d", rc);
+ return rc;
+ }
+
+ do {
+ rc = _publish_test_msg(client, "data", QOS1);
+ if (rc < 0) {
+ Log_e("client publish topic failed :%d.", rc);
+ }
+
+ rc = IOT_MQTT_Yield(client, 2000);
+ if (rc == QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT) {
+ HAL_SleepMs(1000);
+ continue;
+ } else if (rc != QCLOUD_RET_SUCCESS && rc != QCLOUD_RET_MQTT_RECONNECTED) {
+ Log_e("exit with error: %d", rc);
+ break;
+ }
+ } while (!sg_main_exit);
+
+ rc = _unsubscribe_topic(client, "data");
+ rc |= IOT_MQTT_Destroy(&client);
+
+ utils_log_deinit();
+ return rc;
+}
diff --git a/components/connectivity/iot-hub-device-c-sdk/services/common/mqtt_client/src/mqtt_client.c b/components/connectivity/iot-hub-device-c-sdk/services/common/mqtt_client/src/mqtt_client.c
new file mode 100644
index 00000000..a26e2935
--- /dev/null
+++ b/components/connectivity/iot-hub-device-c-sdk/services/common/mqtt_client/src/mqtt_client.c
@@ -0,0 +1,584 @@
+/**
+ * @copyright
+ *
+ * Tencent is pleased to support the open source community by making IoT Hub available.
+ * Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
+ *
+ * Licensed under the MIT License(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @file mqtt_client.c
+ * @brief
+ * @author fancyxu (fancyxu@tencent.com)
+ * @version 1.0
+ * @date 2021-05-28
+ *
+ * @par Change Log:
+ *