feat: 移植腾讯云物联网开发平台 C SDK
This commit is contained in:
@@ -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:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2022-04-19 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "at_module.h"
|
||||
|
||||
/**
|
||||
* @brief AT Command for Iot Hub
|
||||
* 1. network_register:
|
||||
* AT+TCREGNET=?
|
||||
* AT+TCREGNET?
|
||||
* AT+TCREGNET=<module_type>,<action>[,<ssid>,<pw>][,<apn>]
|
||||
* 2. device_info:
|
||||
* AT+TCDEVINFOSET=?
|
||||
* AT+TCDEVINFOSET?
|
||||
* AT+TCDEVINFOSET=<tlsmode>,<productId>,<devicename>[,<devicesecret>][,<certname>]
|
||||
* 3. cert:
|
||||
* AT+TCCERTADD=?
|
||||
* AT+TCCERTADD?
|
||||
* AT+TCCERTADD=<cert_name>,<cert_size>
|
||||
* AT+TCCERTCHECK=?
|
||||
* AT+TCCERTCHECK?
|
||||
* AT+TCCERTCHECK=<cert_name>
|
||||
* AT+TCCERTDEL=?
|
||||
* AT+TCCERTDEL?
|
||||
* AT+TCCERTDEL=<cert_name>
|
||||
* 4. dynamic register:
|
||||
* AT+TCPRDINFOSET=?
|
||||
* AT+TCPRDINFOSET?
|
||||
* AT+TCPRDINFOSET=<tls_mode>,<product_ID>,<product_secret>,<device_name>
|
||||
* AT+TCDEVREG=?
|
||||
* AT+TCDEVREG
|
||||
* 5. module info
|
||||
* AT+TCMODULE
|
||||
* AT+TCRESTORE=?
|
||||
* 6. mqtt
|
||||
* AT+TCMQTTCONN=?
|
||||
* AT+TCMQTTCONN?
|
||||
* AT+TCMQTTCONN=<tlsmode>,<cmdtimeout>,<keepalive>,<clean_session>,<reconnect>
|
||||
* AT+TCMQTTDISCONN=?
|
||||
* AT+TCMQTTDISCONN
|
||||
* AT+TCMQTTPUB=?
|
||||
* AT+TCMQTTPUB=<topic>,<qos>,<message>
|
||||
* AT+TCMQTTPUBL=?
|
||||
* AT+TCMQTTPUBL= <topic>,<qos>,<msg_length>
|
||||
* AT+TCMQTTSUB=?
|
||||
* AT+TCMQTTSUB?
|
||||
* AT+TCMQTTSUB=<topic>,<qos>
|
||||
* AT+TCMQTTUNSUB=?
|
||||
* AT+TCMQTTUNSUB?
|
||||
* AT+TCMQTTUNSUB=<topic>
|
||||
* AT+TCMQTTSTATE=?
|
||||
* AT+TCMQTTSTATE?
|
||||
* 7. ota
|
||||
* AT+TCOTASET=?
|
||||
* AT+TCOTASET?
|
||||
* AT+TCOTASET=<ctlstate>,<fw_ver>
|
||||
* AT+TCFWINFO=?
|
||||
* AT+TCFWINFO?
|
||||
* AT+TCREADFWDATA=?
|
||||
* AT+TCREADFWDATA=<len>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief AT module cmd.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
MODULE_CMD_SET_DEVICE_INFO, // AT+TCDEVINFOSET=<tlsmode>,<productId>,<devicename>[,<devicesecret>][,<certname>]
|
||||
MODULE_CMD_CONNECT, // AT+TCMQTTCONN=<tlsmode>,<cmdtimeout>,<keepalive>,<clean_session>,<reconnect>
|
||||
MODULE_CMD_DISCONNECT, // AT+TCMQTTDISCONN
|
||||
MODULE_CMD_PUBLISH, // AT+TCMQTTPUB=<topic>,<qos>,<message>
|
||||
MODULE_CMD_PUBLISHL, // AT+TCMQTTPUBL=<topic>,<qos>,<msg_length>
|
||||
MODULE_CMD_SUBSCRIBE, // AT+TCMQTTSUB=<topic>,<qos>
|
||||
MODULE_CMD_UNSUBSCRIBE, // AT+TCMQTTUNSUB=<topic>
|
||||
MODULE_CMD_GET_CONNECT_STATUS, // AT+TCMQTTSTATE?
|
||||
MODULE_CMD_SET_OTA_VERSION, // AT+TCOTASET=<ctlstate>,<fw_ver>
|
||||
MODULE_CMD_GET_OTA_FW_INFO, // AT+TCFWINFO?
|
||||
MODULE_CMD_READ_OTA_FW_DATA // AT+TCREADFWDATA=<len>
|
||||
} 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);
|
||||
}
|
@@ -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:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2022-04-25 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
@@ -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:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2022-04-25 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
@@ -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:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2022-04-25 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
@@ -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:
|
||||
* <table>
|
||||
* Date Version Author Description
|
||||
* 2022-06-07 1.0 hubertxxu first commit
|
||||
* </table>
|
||||
*/
|
||||
#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);
|
||||
}
|
@@ -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:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2022-04-24 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
Reference in New Issue
Block a user