
1. qcloud has a great revolution, the protocol has been changed to implement data template, so the old TencentCloud_SDK developed by us will not work fine now(mqtt still works, but data template will not works fine for recently created product/devices). 2. I merge the official qlcoud sdk(include both the iot-hub and iot-explorer sdk) into the componet/conectivity to support new protocol of data template 3. iot-hub sdk, supply the fundamental iot protocol(like mqtt coap, etc.) iot-explorer sdk, supply the high level service like data template based on mqtt 4. To know how it works, see qcloud_iot_explorer_sdk_data_template、qcloud_iot_hub_sdk_mqtt example(keil project in board\TencentOS_tiny_EVB_MX_Plus\KEIL\qcloud_iot_explorer_sdk_data_template and board\TencentOS_tiny_EVB_MX_Plus\KEIL\qcloud_iot_hub_sdk_mqtt)
510 lines
14 KiB
C
510 lines
14 KiB
C
/*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "qcloud_iot_export.h"
|
|
#include "qcloud_iot_import.h"
|
|
#include "lite-utils.h"
|
|
|
|
|
|
#ifdef AUTH_MODE_CERT
|
|
static char sg_cert_file[PATH_MAX + 1]; // full path of device cert file
|
|
static char sg_key_file[PATH_MAX + 1]; // full path of device key file
|
|
#endif
|
|
|
|
|
|
static DeviceInfo sg_devInfo;
|
|
|
|
|
|
#define OTA_BUF_LEN (5000)
|
|
|
|
static bool sg_pub_ack = false;
|
|
static int sg_packet_id = 0;
|
|
|
|
static void event_handler(void *pclient, void *handle_context, MQTTEventMsg *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_SUBCRIBE_SUCCESS:
|
|
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
|
|
break;
|
|
|
|
case MQTT_EVENT_SUBCRIBE_TIMEOUT:
|
|
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
|
|
break;
|
|
|
|
case MQTT_EVENT_SUBCRIBE_NACK:
|
|
Log_i("subscribe 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);
|
|
if (sg_packet_id == packet_id)
|
|
sg_pub_ack = true;
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* demo of firmware info management in device side for resuming download from break point */
|
|
#define VERSION_FILE_PATH "./local_fw_info.json"
|
|
#define KEY_VER "version"
|
|
#define KEY_MD5 "md5"
|
|
#define KEY_SIZE "downloadSize"
|
|
#define KEY_STATE "state"
|
|
#define KEY_PREVER "running_version"
|
|
static char * get_local_fw_version(char **ver, char **md5, char **size)
|
|
{
|
|
#define INFO_FILE_MAX_LEN 256
|
|
|
|
FILE *fp;
|
|
int len;
|
|
int rlen;
|
|
char *preVer;
|
|
char *reportVer = NULL;
|
|
|
|
fp = fopen(VERSION_FILE_PATH, "r");
|
|
if(NULL == fp) {
|
|
Log_e("open file %s failed", VERSION_FILE_PATH);
|
|
goto exit;
|
|
}
|
|
|
|
fseek(fp, 0L, SEEK_END);
|
|
len = ftell(fp);
|
|
if(len > INFO_FILE_MAX_LEN){
|
|
Log_e("%s is too big, pls check", VERSION_FILE_PATH);
|
|
goto exit;
|
|
}
|
|
|
|
char *JsonDoc = (char *)HAL_Malloc(len + 10);
|
|
if(NULL == JsonDoc){
|
|
Log_e("malloc buffer for json file read fail");
|
|
goto exit;
|
|
}
|
|
|
|
rewind(fp);
|
|
rlen = fread(JsonDoc, 1, len, fp);
|
|
|
|
if(len != rlen){
|
|
Log_e("read data len (%d) less than needed (%d), %s", rlen, len, JsonDoc);
|
|
}
|
|
|
|
*ver = LITE_json_value_of(KEY_VER, JsonDoc);
|
|
*md5 = LITE_json_value_of(KEY_MD5, JsonDoc);
|
|
*size = LITE_json_value_of(KEY_SIZE, JsonDoc);
|
|
preVer = LITE_json_value_of(KEY_PREVER, JsonDoc);
|
|
|
|
if((NULL != *ver) && (NULL != preVer) && (0 == strcmp(*ver, preVer))){
|
|
reportVer = *ver;
|
|
HAL_Free(preVer);
|
|
}else{
|
|
reportVer = preVer;
|
|
}
|
|
|
|
exit:
|
|
|
|
if(NULL != fp){
|
|
fclose(fp);
|
|
}
|
|
|
|
return reportVer;
|
|
#undef INFO_FILE_MAX_LEN
|
|
}
|
|
|
|
/* update local firmware info for resuming download from break point */
|
|
static int update_local_fw_info(const char *version, const char *preVer, const char *md5, uint32_t downloadedSize)
|
|
{
|
|
#define INFO_FILE_MAX_LEN 256
|
|
|
|
FILE *fp;
|
|
int wlen;
|
|
int ret = QCLOUD_RET_SUCCESS;
|
|
char dataBuff[INFO_FILE_MAX_LEN];
|
|
|
|
memset(dataBuff, 0, INFO_FILE_MAX_LEN);
|
|
HAL_Snprintf(dataBuff, INFO_FILE_MAX_LEN, "{\"%s\":\"%s\", \"%s\":\"%s\",\"%s\":%d,\"%s\":\"%s\"}",\
|
|
KEY_VER, version, KEY_MD5, md5, KEY_SIZE, downloadedSize, \
|
|
KEY_PREVER, (NULL == preVer)?"1.0.0": preVer);
|
|
|
|
fp = fopen(VERSION_FILE_PATH, "w");
|
|
if(NULL == fp) {
|
|
Log_e("open file %s failed", VERSION_FILE_PATH);
|
|
ret = QCLOUD_ERR_FAILURE;
|
|
goto exit;
|
|
}
|
|
|
|
wlen = fwrite(dataBuff, 1, strlen(dataBuff), fp);
|
|
if(wlen != strlen(dataBuff)){
|
|
Log_e("save version to file err");
|
|
ret = QCLOUD_ERR_FAILURE;
|
|
}
|
|
|
|
exit:
|
|
|
|
if(NULL != fp){
|
|
fclose(fp);
|
|
}
|
|
|
|
return ret;
|
|
#undef INFO_FILE_MAX_LEN
|
|
}
|
|
|
|
/* get local firmware offset for resuming download from break point */
|
|
static int getFwOffset(void *h_ota, char *local_ver, char *local_md5, char *local_size, uint32_t *offset, uint32_t *size_file)
|
|
{
|
|
char version[128], md5sum[33];
|
|
uint32_t local_len;
|
|
int Ret;
|
|
|
|
Ret = IOT_OTA_Ioctl(h_ota, IOT_OTAG_VERSION, version, 128);
|
|
Ret |= IOT_OTA_Ioctl(h_ota, IOT_OTAG_MD5SUM, md5sum, 33);
|
|
Ret |= IOT_OTA_Ioctl(h_ota, IOT_OTAG_FILE_SIZE, size_file, 4);
|
|
|
|
local_len = (NULL == local_size)?0:atoi(local_size);
|
|
|
|
|
|
if((NULL == local_ver)||(NULL == local_md5)||(NULL == local_size)){
|
|
*offset = 0;
|
|
}
|
|
else if((0 != strcmp(local_ver, version))||(0 != strcmp(local_md5, md5sum))||(local_len > *size_file)){
|
|
*offset = 0;
|
|
}else {
|
|
*offset = local_len;
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
/* calculate left MD5 for resuming download from break point */
|
|
static int cal_exist_fw_md5(void *h_ota, FILE *fp, size_t size){
|
|
#define BUFF_LEN 1024
|
|
|
|
char buff[BUFF_LEN];
|
|
size_t rlen;
|
|
int Ret = QCLOUD_RET_SUCCESS;
|
|
|
|
while((size > 0) && (!feof(fp))){
|
|
rlen = (size > BUFF_LEN)?BUFF_LEN:size;
|
|
if(rlen != fread(buff, 1, rlen, fp)){
|
|
Log_e("read data len not expected");
|
|
Ret = QCLOUD_ERR_FAILURE;
|
|
break;
|
|
}
|
|
IOT_OTA_UpdateClientMd5(h_ota, buff, rlen);
|
|
size -= rlen;
|
|
}
|
|
return Ret;
|
|
#undef BUFF_LEN
|
|
}
|
|
|
|
static int _setup_connect_init_params(MQTTInitParams* initParams)
|
|
{
|
|
int ret;
|
|
|
|
ret = HAL_GetDevInfo((void *)&sg_devInfo);
|
|
if(QCLOUD_RET_SUCCESS != ret){
|
|
return ret;
|
|
}
|
|
|
|
initParams->device_name = sg_devInfo.device_name;
|
|
initParams->product_id = sg_devInfo.product_id;
|
|
|
|
#ifdef AUTH_MODE_CERT
|
|
char certs_dir[PATH_MAX + 1] = "certs";
|
|
char current_path[PATH_MAX + 1];
|
|
char *cwd = getcwd(current_path, sizeof(current_path));
|
|
if (cwd == NULL)
|
|
{
|
|
Log_e("getcwd return NULL");
|
|
return QCLOUD_ERR_FAILURE;
|
|
}
|
|
sprintf(sg_cert_file, "%s/%s/%s", current_path, certs_dir, sg_devInfo.dev_cert_file_name);
|
|
sprintf(sg_key_file, "%s/%s/%s", current_path, certs_dir, sg_devInfo.dev_key_file_name);
|
|
|
|
initParams->cert_file = sg_cert_file;
|
|
initParams->key_file = sg_key_file;
|
|
#else
|
|
initParams->device_secret = sg_devInfo.device_secret;
|
|
#endif
|
|
|
|
|
|
initParams->command_timeout = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT;
|
|
initParams->keep_alive_interval_ms = QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL;
|
|
initParams->auto_connect_enable = 1;
|
|
initParams->event_handle.h_fp = event_handler;
|
|
|
|
return QCLOUD_RET_SUCCESS;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
IOT_Log_Set_Level(eLOG_DEBUG);
|
|
int rc;
|
|
uint32_t firmware_valid;
|
|
char version[128], md5sum[33];
|
|
uint32_t size_downloaded, size_file;
|
|
int len;
|
|
int ota_over = 0;
|
|
bool upgrade_fetch_success = true;
|
|
char buf_ota[OTA_BUF_LEN];
|
|
FILE *fp = NULL;
|
|
uint32_t offset = 0;
|
|
|
|
|
|
MQTTInitParams init_params = DEFAULT_MQTTINIT_PARAMS;
|
|
rc = _setup_connect_init_params(&init_params);
|
|
if (rc != QCLOUD_RET_SUCCESS) {
|
|
Log_e("init params err,rc=%d", rc);
|
|
return rc;
|
|
}
|
|
|
|
void *client = IOT_MQTT_Construct(&init_params);
|
|
if (client != NULL) {
|
|
Log_i("Cloud Device Construct Success");
|
|
} else {
|
|
Log_e("Cloud Device Construct Failed");
|
|
return QCLOUD_ERR_FAILURE;
|
|
}
|
|
|
|
void *h_ota = IOT_OTA_Init(sg_devInfo.product_id, sg_devInfo.device_name, client);
|
|
if (NULL == h_ota) {
|
|
Log_e("initialize OTA failed");
|
|
return QCLOUD_ERR_FAILURE;
|
|
}
|
|
|
|
IOT_MQTT_Yield(client, 1000); //make sure subscribe success
|
|
|
|
char *local_ver = NULL, *local_md5 = NULL, *local_size = NULL, *reportVer = NULL;
|
|
reportVer = get_local_fw_version(&local_ver, &local_md5, &local_size);
|
|
Log_d("local_ver:%s local_md5:%s, local_size:%s", local_ver, local_md5, local_size);
|
|
|
|
/* Must report version first */
|
|
if (0 > IOT_OTA_ReportVersion(h_ota, (NULL == reportVer)?"1.0.0": reportVer)) {
|
|
Log_e("report OTA version failed");
|
|
goto exit;
|
|
}
|
|
|
|
do {
|
|
Log_i("wait for ota upgrade command...");
|
|
|
|
IOT_MQTT_Yield(client, 200);
|
|
|
|
if (IOT_OTA_IsFetching(h_ota)) {
|
|
|
|
/*check pre-download finished or not*/
|
|
/*if version & MD5 is the same,then pre-download not finished,get breakpoint continue download.otherwise the version is new*/
|
|
rc = getFwOffset(h_ota, local_ver, local_md5, local_size, &offset, &size_file);
|
|
if(QCLOUD_RET_SUCCESS != rc){
|
|
Log_e("get fw offset err,rc:%d",rc);
|
|
upgrade_fetch_success = false;
|
|
break;
|
|
}
|
|
|
|
/*cal file md5*/
|
|
//Log_d("Get offset:%d(%x)", offset, offset);
|
|
if(offset > 0){
|
|
if (NULL == (fp = fopen("ota.bin", "ab+"))) {
|
|
Log_e("open file failed");
|
|
upgrade_fetch_success = false;
|
|
break;
|
|
}
|
|
|
|
if(QCLOUD_RET_SUCCESS != cal_exist_fw_md5(h_ota, fp, offset)){
|
|
Log_e("cal exist fw md5 failed");
|
|
upgrade_fetch_success = false;
|
|
break;
|
|
}
|
|
}else{
|
|
if (NULL == (fp = fopen("ota.bin", "wb+"))) {
|
|
Log_e("open file failed");
|
|
upgrade_fetch_success = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*set offset and start http connect*/
|
|
fseek(fp, offset, SEEK_SET);
|
|
rc = IOT_OTA_StartDownload(h_ota, offset, size_file);
|
|
if(QCLOUD_RET_SUCCESS != rc){
|
|
Log_e("OTA download start err,rc:%d",rc);
|
|
upgrade_fetch_success = false;
|
|
break;
|
|
}
|
|
|
|
do {
|
|
len = IOT_OTA_FetchYield(h_ota, buf_ota, OTA_BUF_LEN, 1);
|
|
if (len > 0) {
|
|
if (1 != fwrite(buf_ota, len, 1, fp)) {
|
|
Log_e("write data to file failed");
|
|
upgrade_fetch_success = false;
|
|
break;
|
|
}
|
|
} else if (len < 0) {
|
|
Log_e("download fail rc=%d", len);
|
|
upgrade_fetch_success = false;
|
|
break;
|
|
}
|
|
fflush(fp);
|
|
|
|
/* get OTA information and update local info */
|
|
IOT_OTA_Ioctl(h_ota, IOT_OTAG_FETCHED_SIZE, &size_downloaded, 4);
|
|
IOT_OTA_Ioctl(h_ota, IOT_OTAG_FILE_SIZE, &size_file, 4);
|
|
IOT_OTA_Ioctl(h_ota, IOT_OTAG_MD5SUM, md5sum, 33);
|
|
IOT_OTA_Ioctl(h_ota, IOT_OTAG_VERSION, version, 128);
|
|
rc = update_local_fw_info(version, reportVer, md5sum, size_downloaded);
|
|
if(QCLOUD_RET_SUCCESS != rc){
|
|
Log_e("update local fw info err,rc:%d", rc);
|
|
}
|
|
|
|
IOT_MQTT_Yield(client, 100);
|
|
|
|
} while (!IOT_OTA_IsFetchFinish(h_ota));
|
|
|
|
fclose(fp);
|
|
fp = NULL;
|
|
|
|
/* Must check MD5 match or not */
|
|
if (upgrade_fetch_success) {
|
|
IOT_OTA_Ioctl(h_ota, IOT_OTAG_CHECK_FIRMWARE, &firmware_valid, 4);
|
|
if (0 == firmware_valid) {
|
|
Log_e("The firmware is invalid");
|
|
upgrade_fetch_success = false;
|
|
rc = update_local_fw_info(NULL, reportVer, NULL, 0);
|
|
if(QCLOUD_RET_SUCCESS != rc){
|
|
Log_e("update local fw info err,rc:%d", rc);
|
|
}
|
|
} else {
|
|
Log_i("The firmware is valid");
|
|
upgrade_fetch_success = true;
|
|
}
|
|
}
|
|
|
|
ota_over = 1;
|
|
}
|
|
|
|
HAL_SleepMs(2000);
|
|
} while(!ota_over);
|
|
|
|
|
|
if (upgrade_fetch_success)
|
|
{
|
|
/* begin execute OTA files, should report upgrade begin */
|
|
sg_packet_id = IOT_OTA_ReportUpgradeBegin(h_ota);
|
|
if (0 > sg_packet_id) {
|
|
Log_e("report OTA begin failed error:%d", sg_packet_id);
|
|
return QCLOUD_ERR_FAILURE;
|
|
}
|
|
while (!sg_pub_ack) {
|
|
HAL_SleepMs(1000);
|
|
IOT_MQTT_Yield(client, 200);
|
|
}
|
|
sg_pub_ack = false;
|
|
|
|
//* add your own upgrade logic here*//
|
|
//* fw_upgrade.....
|
|
|
|
if(QCLOUD_RET_SUCCESS == rc){
|
|
/* if upgrade success */
|
|
/* after execute OTA files, should report upgrade result */
|
|
sg_packet_id = IOT_OTA_ReportUpgradeSuccess(h_ota, NULL);
|
|
if (0 > sg_packet_id) {
|
|
Log_e("report OTA result failed error:%d", sg_packet_id);
|
|
return QCLOUD_ERR_FAILURE;
|
|
}
|
|
while (!sg_pub_ack) {
|
|
HAL_SleepMs(1000);
|
|
IOT_MQTT_Yield(client, 200);
|
|
}
|
|
rc = update_local_fw_info(version, version, md5sum, size_downloaded); // just for example, add your own logic
|
|
sg_pub_ack = false;
|
|
|
|
}else{
|
|
/* if upgrade fail */
|
|
sg_packet_id = IOT_OTA_ReportUpgradeFail(h_ota, NULL);
|
|
if (0 > sg_packet_id) {
|
|
Log_e("report OTA result failed error:%d", sg_packet_id);
|
|
return QCLOUD_ERR_FAILURE;
|
|
}
|
|
while (!sg_pub_ack) {
|
|
HAL_SleepMs(1000);
|
|
IOT_MQTT_Yield(client, 200);
|
|
}
|
|
rc = update_local_fw_info(NULL, reportVer, NULL, 0); // just for example, add your own logic
|
|
sg_pub_ack = false;
|
|
|
|
}
|
|
}
|
|
|
|
exit:
|
|
|
|
if(NULL != fp){
|
|
fclose(fp);
|
|
fp = NULL;
|
|
}
|
|
|
|
if(NULL != local_ver){
|
|
reportVer = (reportVer == local_ver)?NULL: reportVer;
|
|
HAL_Free(local_ver);
|
|
local_ver = NULL;
|
|
}
|
|
|
|
if(NULL != local_md5){
|
|
HAL_Free(local_md5);
|
|
local_md5 = NULL;
|
|
}
|
|
|
|
if(NULL != local_size){
|
|
HAL_Free(local_size);
|
|
local_size = NULL;
|
|
}
|
|
|
|
if(NULL != reportVer){
|
|
HAL_Free(reportVer);
|
|
reportVer = NULL;
|
|
}
|
|
|
|
IOT_OTA_Destroy(h_ota);
|
|
|
|
IOT_MQTT_Destroy(&client);
|
|
|
|
return 0;
|
|
}
|