/* * Tencent is pleased to support the open source community by making IoT Hub available. * Copyright (C) 2016 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. * */ #ifdef __cplusplus extern "C" { #endif #include "qcloud_iot_export.h" #include "qcloud_iot_import.h" #ifdef OTA_MQTT_CHANNEL #include #include "ota_client.h" /* OSC, OTA signal channel */ typedef struct { void *mqtt; // MQTT cient const char *product_id; const char *device_name; char topic_upgrade[OTA_MAX_TOPIC_LEN]; // OTA MQTT Topic OnOTAMessageCallback msg_callback; void *context; bool topic_ready; } OTA_MQTT_Struct_t; /* Generate topic name according to @OTATopicType, @productId, @deviceName */ /* and then copy to @buf. */ /* 0, successful; -1, failed */ static int _otamqtt_gen_topic_name(char *buf, size_t bufLen, const char *OTATopicType, const char *productId, const char *deviceName) { IOT_FUNC_ENTRY; int ret; ret = HAL_Snprintf(buf, bufLen, "$ota/%s/%s/%s", OTATopicType, productId, deviceName); if (ret >= bufLen) IOT_FUNC_EXIT_RC(IOT_OTA_ERR_FAIL); if (ret < 0) { Log_e("HAL_Snprintf failed"); IOT_FUNC_EXIT_RC(IOT_OTA_ERR_FAIL); } IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS); } /* report progress of OTA */ static int _otamqtt_publish(OTA_MQTT_Struct_t *handle, const char *topicType, int qos, const char *msg) { IOT_FUNC_ENTRY; int ret; char topic_name[OTA_MAX_TOPIC_LEN]; PublishParams pub_params = DEFAULT_PUB_PARAMS; if (0 == qos) { pub_params.qos = QOS0; } else { pub_params.qos = QOS1; } pub_params.payload = (void *)msg; pub_params.payload_len = strlen(msg); /* inform OTA to topic: "/ota/device/progress/$(product_id)/$(device_name)" */ ret = _otamqtt_gen_topic_name(topic_name, OTA_MAX_TOPIC_LEN, topicType, handle->product_id, handle->device_name); if (ret < 0) { Log_e("generate topic name of info failed"); IOT_FUNC_EXIT_RC(IOT_OTA_ERR_FAIL); } ret = IOT_MQTT_Publish(handle->mqtt, topic_name, &pub_params); if (ret < 0) { Log_e("publish to topic: %s failed", topic_name); IOT_FUNC_EXIT_RC(IOT_OTA_ERR_OSC_FAILED); } IOT_FUNC_EXIT_RC(ret); } /* callback after OTA topic is subscribed */ /* Parse firmware info (version/URL/file size/MD5) from JSON text */ static void _otamqtt_upgrage_cb(void *pClient, MQTTMessage *message, void *pcontext) { OTA_MQTT_Struct_t *handle = (OTA_MQTT_Struct_t *)pcontext; Log_d("topic=%.*s", message->topic_len, message->ptopic); Log_i("len=%u, topic_msg=%.*s", message->payload_len, message->payload_len, (char *)message->payload); if (NULL != handle->msg_callback) { handle->msg_callback(handle->context, message->payload, message->payload_len); } } static void _otamqtt_event_callback(void *pclient, MQTTEventType event_type, void *user_data) { OTA_MQTT_Struct_t *h_osc = (OTA_MQTT_Struct_t *)user_data; switch (event_type) { case MQTT_EVENT_SUBCRIBE_SUCCESS: Log_d("OTA topic subscribe success"); h_osc->topic_ready = true; break; case MQTT_EVENT_SUBCRIBE_TIMEOUT: Log_i("OTA topic subscribe timeout"); h_osc->topic_ready = false; break; case MQTT_EVENT_SUBCRIBE_NACK: Log_i("OTA topic subscribe NACK"); h_osc->topic_ready = false; break; case MQTT_EVENT_UNSUBSCRIBE: Log_i("OTA topic has been unsubscribed"); h_osc->topic_ready = false; ; break; case MQTT_EVENT_CLIENT_DESTROY: Log_i("mqtt client has been destroyed"); h_osc->topic_ready = false; ; break; default: return; } } void *qcloud_osc_init(const char *productId, const char *deviceName, void *channel, OnOTAMessageCallback callback, void *context) { int ret; OTA_MQTT_Struct_t *h_osc = NULL; if (NULL == (h_osc = HAL_Malloc(sizeof(OTA_MQTT_Struct_t)))) { Log_e("allocate for h_osc failed"); goto do_exit; } memset(h_osc, 0, sizeof(OTA_MQTT_Struct_t)); /* subscribe the OTA topic: "$ota/update/$(product_id)/$(device_name)" */ ret = _otamqtt_gen_topic_name(h_osc->topic_upgrade, OTA_MAX_TOPIC_LEN, "update", productId, deviceName); if (ret < 0) { Log_e("generate topic name of upgrade failed"); goto do_exit; } SubscribeParams sub_params = DEFAULT_SUB_PARAMS; sub_params.on_message_handler = _otamqtt_upgrage_cb; sub_params.on_sub_event_handler = _otamqtt_event_callback; sub_params.qos = QOS1; sub_params.user_data = h_osc; ret = IOT_MQTT_Subscribe(channel, h_osc->topic_upgrade, &sub_params); if (ret < 0) { Log_e("ota mqtt subscribe failed!"); goto do_exit; } int wait_cnt = 10; while (!h_osc->topic_ready && (wait_cnt > 0)) { // wait for subscription result IOT_MQTT_Yield_MT(channel, 200); wait_cnt--; } if (wait_cnt == 0) { Log_e("ota mqtt subscribe timeout!"); goto do_exit; } h_osc->mqtt = channel; h_osc->product_id = productId; h_osc->device_name = deviceName; h_osc->msg_callback = callback; h_osc->context = context; return h_osc; do_exit: if (NULL != h_osc) { HAL_Free(h_osc); } return NULL; } int qcloud_osc_deinit(void *handle) { IOT_FUNC_ENTRY; int ret = QCLOUD_RET_SUCCESS; if (NULL != handle) { OTA_MQTT_Struct_t *h_osc = (OTA_MQTT_Struct_t *)handle; ret = IOT_MQTT_Unsubscribe(h_osc->mqtt, h_osc->topic_upgrade); HAL_Free(handle); } IOT_FUNC_EXIT_RC(ret); } /* report progress of OTA */ int qcloud_osc_report_progress(void *handle, const char *msg) { return _otamqtt_publish(handle, "report", QOS0, msg); } /* report version of OTA firmware */ int qcloud_osc_report_version(void *handle, const char *msg) { return _otamqtt_publish(handle, "report", QOS1, msg); } /* report upgrade begin of OTA firmware */ int qcloud_osc_report_upgrade_result(void *handle, const char *msg) { return _otamqtt_publish(handle, "report", QOS1, msg); } #endif #ifdef __cplusplus } #endif