/* * Copyright (c) 2014-2016 Alibaba Group. All rights reserved. * License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 #include #include #include "ota.h" #include "cJSON.h" #include "time.h" #include "cfg.h" #include "esp_mqtt.h" #include "esp_MSG_ctrl.h" #include "iot_import.h" #include "iot_export.h" #include "log.h" #define string_content "msgContent" #define string_wifiOTAInfo "wifiOTAInfo" #define string_mcuOTAInfo "mcuOTAInfo" #define string_OTA_URL "url" #define string_OTA_MD5 "MD5" #define string_OTA_TOTALSIZE "totalLength" #define string_OTA_SEGSIZE "segmentLength" #define string_OTA_MODE "otaMode" #define string_OTA_VERSION "ver" int ota_report_latest_version = 0; // read buffer by byte still delimiter appear total_count times // return delimiter current position in buffer // return < 0 means could not find delim for total_count times static int read_until_counts(char *buffer, char delim, int len, int total_count) { /*TODO: buffer check, delim check, count check,further: do an buffer length limited*/ int i = 0, valid_count = 0; for (i = 0; i < len; ++i) { if (buffer[i] == delim) { valid_count++; } if (valid_count == total_count) { return i; } } return -1; } #if (MQTT_SERVER == MQTT_SERVER_NETEASE) int upgrade_message_parse(cJSON *info_item, ota_info_t *ota_info) { cJSON *url_item, *version_item, *totalSize_item, *segSize_item, *md5_item; ip_addr_t target_ip; int ret = 0; totalSize_item = cJSON_GetObjectItem(info_item, string_OTA_TOTALSIZE); if (!totalSize_item) { OTA_LOG_EX(LOG_Error, "get total size item\r\n"); return -1; } segSize_item = cJSON_GetObjectItem(info_item, string_OTA_SEGSIZE); if (!segSize_item) { OTA_LOG_EX(LOG_Error, "get segment size item\r\n"); return -1; } md5_item = cJSON_GetObjectItem(info_item, string_OTA_MD5); if (!md5_item) { OTA_LOG_EX(LOG_Error, "get MD5 item\r\n"); return -1; } version_item = cJSON_GetObjectItem(info_item, string_OTA_VERSION); if (!version_item) { OTA_LOG_EX(LOG_Error, "get version item"); return -1; } url_item = cJSON_GetObjectItem(info_item, string_OTA_URL); if (!url_item) { OTA_LOG_EX(LOG_Error, "get url item\r\n"); return -1; } url_item = cJSON_GetObjectItem(info_item, string_OTA_URL); if (!url_item) { OTA_LOG_EX(LOG_Error, "get url item\r\n"); return -1; } char *p = url_item->valuestring; // p pointer point at the beginning of whole URL string // fetch domain name for URL by delimiter '/' int start_pos = read_until_counts(p, '/', strlen(p), 2); int end_pos = read_until_counts(p, '/', strlen(p), 3); char *target_name = (char *)zalloc(end_pos - start_pos); memcpy(target_name, p + start_pos + 1, end_pos - start_pos - 1); /* get ip by hostname */ OTA_LOG_EX(LOG_Info,"ota host: %s\r\n", target_name); do { ret = netconn_gethostbyname(target_name, &target_ip); } while (ret); OTA_LOG_EX(LOG_Info,"ota ip got:\r\n"); /* save ota information to flash, load it after system restart */ memset(ota_info, 0, sizeof(ota_info_t)); ota_info->ota_flag = 1; ota_info->target_ip = target_ip; ota_info->port = REMOTE_OTA_SERVER_PORT; ota_info->bin_size = totalSize_item->valueint; ota_info->seg_size = segSize_item->valueint; memcpy(ota_info->md5, md5_item->valuestring, MAX_MD5_LEN); sprintf(ota_info->latest_version,"%d", version_item->valueint); ota_info->lastest_version_int = version_item->valueint; memcpy(ota_info->hostname, target_name, strlen(target_name)); memcpy(ota_info->ota_path, p + end_pos, strlen(p) - end_pos); return 0; } // callback of upgrade topic void upgrade_message_arrive(void *pcontext, void *pclient, iotx_mqtt_event_msg_pt msg) { iotx_mqtt_topic_info_pt ptopic_info = (iotx_mqtt_topic_info_pt) msg->msg; cJSON *root, *content_item, *wifo_info_item, * mcu_info_item; ota_info_t *mcu_ota_info = 0; ota_info_t *wifi_ota_info = 0; int rc; /* print topic name and topic message */ OTA_LOG_EX(LOG_Info, "upgrade topic %s, len %x\r\n", ptopic_info->ptopic, ptopic_info->topic_len); OTA_LOG_EX(LOG_Info, "upgrade payload %s, len %x\r\n", ptopic_info->payload, ptopic_info->payload_len); root = cJSON_Parse(ptopic_info->payload); if (!root) { OTA_LOG_EX(LOG_Error, "Error before: [%s]\r\n", cJSON_GetErrorPtr()); return; } /* content json parse */ content_item = cJSON_GetObjectItem(root, string_content); if (!root) { OTA_LOG_EX(LOG_Error, "get content failed\r\n"); return; } mcu_info_item = cJSON_GetObjectItem(content_item, string_mcuOTAInfo); if (mcu_info_item) { mcu_ota_info = HAL_Malloc(sizeof(ota_info_t)); rc = upgrade_message_parse(mcu_info_item, mcu_ota_info); if (rc < 0) { HAL_Free(mcu_ota_info); mcu_ota_info = 0; } } else { OTA_LOG_EX(LOG_Info, "Do not get MCU info item\r\n"); } wifo_info_item = cJSON_GetObjectItem(content_item, string_wifiOTAInfo); if (wifo_info_item) { wifi_ota_info = HAL_Malloc(sizeof(ota_info_t)); rc = upgrade_message_parse(wifo_info_item, wifi_ota_info); if (rc < 0) { HAL_Free(wifi_ota_info); wifi_ota_info = 0; } } else { OTA_LOG_EX(LOG_Info, "Do not get wifi info item\r\n"); } /* report mcu ota information */ if (mcu_ota_info) { ota_mqtt_setInfo(pclient, mcu_ota_info, OTA_DEVICE_MCU); HAL_Free(mcu_ota_info); } /* report wifi ota information */ if (wifi_ota_info) { ota_mqtt_setInfo(pclient, wifi_ota_info, OTA_DEVICE_WIFI); HAL_Free(wifi_ota_info); } cJSON_Delete(root); } // construct report version topic message in json format void construct_report_version_info(iotx_mqtt_topic_info_t *topic_msg, const char *version_info, char *msg_pub) { char MCU_Vesion_info[FIRMWARE_VERSION_MAXLEN]; int msg_len = 0; HAL_GetMCUFirmwareVesion(MCU_Vesion_info); memset(msg_pub, 0, MSG_PUB_MAX_SIZE); sprintf(msg_pub, MQTT_PUB_OTA, version_info, MCU_Vesion_info); msg_len = strlen(msg_pub); topic_msg->payload = (void *)msg_pub; topic_msg->payload_len = msg_len; } // construct report process topic message in json format void construct_report_process_info(char *msg_pub, int type) { int msg_len = 0; int content_type; content_type = 0; if (type == OTA_DEVICE_WIFI) content_type = 1; if (type == OTA_DEVICE_MCU) content_type = 2; sprintf(msg_pub, MQTT_PUB_OTA_START, content_type); msg_len = strlen(msg_pub); } void construct_report_process_info_shadow(char *msg_pub, int type) { int msg_len = 0; msg_pub[0] = '0'; if (type == OTA_DEVICE_WIFI) msg_pub[0] = '1'; if (type == OTA_DEVICE_MCU) msg_pub[0] = '2'; msg_pub[1] = 0; msg_len = strlen(msg_pub); } #endif #if (MQTT_SERVER == MQTT_SERVER_ALIBABA) // callback of upgrade topic void upgrade_message_arrive(void *pcontext, void *pclient, iotx_mqtt_event_msg_pt msg) { iotx_mqtt_topic_info_pt ptopic_info = (iotx_mqtt_topic_info_pt) msg->msg; cJSON *root, *data_item, *size_item, *version_item, *url_item; int ret = 0; ip_addr_t target_ip; ota_info_t *ota_info; /* print topic name and topic message */ OTA_LOG_EX(LOG_Info, "upgrade topic %s, len %x\r\n", ptopic_info->ptopic, ptopic_info->topic_len); OTA_LOG_EX(LOG_Info, "upgrade payload %s, len %x\r\n", ptopic_info->payload, ptopic_info->payload_len); // parse payload which is json format, save it to flash // ota json format should obey https://help.aliyun.com/document_detail/58106.html?spm=a2c4g.11174283.6.569.JBBgOw root = cJSON_Parse((char *)ptopic_info->payload); if (!root) { OTA_LOG_EX(LOG_Error,"Error before: [%s]\r\n", cJSON_GetErrorPtr()); } data_item = cJSON_GetObjectItem(root, "data"); if (!data_item) { OTA_LOG_EX(LOG_Error,"get data item\r\n"); } size_item = cJSON_GetObjectItem(data_item, "size"); if (!size_item) { OTA_LOG_EX(LOG_Error,"get size item\r\n"); } version_item = cJSON_GetObjectItem(data_item, "version"); if (!version_item) { OTA_LOG_EX(LOG_Error,"get version item\r\n"); } url_item = cJSON_GetObjectItem(data_item, "url"); if (!url_item) { OTA_LOG_EX(LOG_Error,"get url item\r\n"); } char *p = url_item->valuestring; // p pointer point at the beginning of whole URL string // fetch domain name for URL by delimiter '/' int start_pos = read_until_counts(p, '/', strlen(p), 2); int end_pos = read_until_counts(p, '/', strlen(p), 3); char *target_name = (char *)zalloc(end_pos - start_pos); memcpy(target_name, p + start_pos + 1, end_pos - start_pos - 1); /* get ip by hostname */ OTA_LOG_EX(LOG_Info,"ota host: %s\r\n", target_name); do { ret = netconn_gethostbyname(target_name, &target_ip); } while (ret); OTA_LOG_EX(LOG_Info,"ota ip got:\r\n"); /* save ota information to flash, load it after system restart */ ota_info = HAL_Malloc(sizeof(ota_info_t)); memset(ota_info, 0, sizeof(ota_info_t)); ota_info->ota_flag = 1; ota_info->target_ip = target_ip; ota_info->port = REMOTE_OTA_SERVER_PORT; ota_info->bin_size = data_item->child->valueint; memcpy(ota_info->latest_version, version_item->valuestring, strlen(version_item->valuestring)); memcpy(ota_info->hostname, target_name, strlen(target_name)); memcpy(ota_info->ota_path, p + end_pos, strlen(p) - end_pos); ota_mqtt_setInfo(pclient, ota_info, OTA_DEVICE_WIFI); HAL_Free(ota_info); free(target_name); } // construct report version topic message in json format void construct_report_version_info(iotx_mqtt_topic_info_t *topic_msg, const char *version_info, char *msg_pub) { int msg_len = 0; cJSON *root = cJSON_CreateObject(); cJSON *version = cJSON_CreateObject(); cJSON *iNumber = cJSON_CreateNumber(1); // reserved, message ID cJSON_AddItemToObject(root, "id", iNumber); cJSON_AddItemToObject(version, "version", cJSON_CreateString((const char *)version_info)); cJSON_AddItemToObject(root, "params", version); char *str = cJSON_Print(root); msg_len = strlen(str); // copy json to publish buffer memcpy(msg_pub, str, msg_len); topic_msg->payload = (void *)msg_pub; topic_msg->payload_len = msg_len; cJSON_Delete(root); free(str); str = NULL; } // construct report process topic message in json format void construct_report_process_info(iotx_mqtt_topic_info_t *topic_msg, char *msg_pub) { int msg_len = 0; memset(topic_msg, 0x0, sizeof(iotx_mqtt_topic_info_t)); cJSON *root = cJSON_CreateObject(); cJSON *params = cJSON_CreateObject(); cJSON *iNumber = cJSON_CreateNumber(1); // reserved, message ID cJSON_AddItemToObject(root, "id", iNumber); char c[3] = {0}; sprintf(c, "%d", 100); cJSON_AddItemToObject(params, "step", cJSON_CreateString((const char *)c)); cJSON_AddItemToObject(params, "desc", cJSON_CreateString("process")); cJSON_AddItemToObject(root, "params", params); char *str = cJSON_Print(root); msg_len = strlen(str); memcpy(msg_pub, str, msg_len); topic_msg->payload = (void *)msg_pub; topic_msg->payload_len = msg_len; cJSON_Delete(root); free(str); str = NULL; } #endif int ota_mqtt_setup(void *pclient) { int rc = 0; iotx_mqtt_topic_info_t topic_msg; char version_info[FIRMWARE_VERSION_MAXLEN]; int get_len; char *msg_pub; /* Subscribe the upgrade topic */ rc = mqtt_client_subscribe(pclient, SUB_TYPE_OTA_UPGRADE, upgrade_message_arrive); if (rc < 0) { OTA_LOG_EX(LOG_Error, "MQTT_Sub UPGRADE failed, rc = %d", rc); } /* choose current time as current version and report */ get_len = HAL_GetFirmwareVesion(version_info); if (get_len <= 0) { OTA_LOG_EX(LOG_Error, "get fimware err:%d\r\n", get_len); } OTA_LOG_EX(LOG_Info, "Firmware Version %s\r\n", version_info); /* construct report version info and set to topic_msg */ msg_pub = HAL_Malloc(MSG_PUB_MAX_SIZE); memset(msg_pub, 0, MSG_PUB_MAX_SIZE); construct_report_version_info(&topic_msg, (const char *)version_info, msg_pub); rc = mqtt_client_publish(pclient, PUB_TYPE_OTA_INFORM, msg_pub); if (rc < 0) { OTA_LOG_EX(LOG_Error, "publish error, rc:%d\r\n", rc); } HAL_Free(msg_pub); IOT_MQTT_Yield(pclient, 200); /* constuct report OTA End */ #if (MQTT_SERVER == MQTT_SERVER_NETEASE) if (pclient){ iotx_mqtt_topic_info_t topic_msg; msg_pub = HAL_Malloc(MSG_PUB_MAX_SIZE); memset(msg_pub, 0, MSG_PUB_MAX_SIZE); construct_report_process_info_shadow(msg_pub, 0xff); OTA_LOG_EX(LOG_Info, "OTA Report Message %s\r\n", msg_pub); rc = mqtt_client_publish(pclient, PUB_TYPE_SHADOW_OTA_INFO, msg_pub); if (rc < 0) { HAL_Free(msg_pub); OTA_LOG_EX(LOG_Error,"error occur when publish,rc:%d\r\n", rc); return rc; } HAL_Free(msg_pub); IOT_MQTT_Yield(pclient, 200); } #endif return 0; } int ota_mqtt_setInfo(void *pclient, ota_info_t *ota_info, int type) { int rc = 0; OTA_LOG_EX(LOG_Info, "OTA setInfo %x, %x\r\n", type, ota_info->ota_flag); remote_ota_store_info(type, ota_info); if (type == OTA_DEVICE_WIFI) { struct OTA_Start_Cmd cmd; cmd.type = OTA_DEVICE_WIFI; MSG_ctrl_cmd_put(MSG_CONTROL_CMD_OTA_START, &cmd); } else { rc = MSG_ctrl_rsp_OTAInfo(type, ota_info); } return rc; } int ota_mqtt_start_execute(void *pclient, int type) { int rc = 0; OTA_LOG_EX(LOG_Info, "OTA start %x\r\n", system_upgrade_flag_check()); /* report progress if needed */ #if (MQTT_SERVER == MQTT_SERVER_NETEASE) if (pclient){ char *msg_pub; msg_pub = HAL_Malloc(MSG_PUB_MAX_SIZE); memset(msg_pub, 0, MSG_PUB_MAX_SIZE); construct_report_process_info(msg_pub, type); OTA_LOG_EX(LOG_Info, "OTA Report Message %s\r\n", msg_pub); rc = mqtt_client_publish(pclient, PUB_TYPE_OTA_INFORM, msg_pub); if (rc < 0) { HAL_Free(msg_pub); OTA_LOG_EX(LOG_Error,"error occur when publish,rc:%d\r\n", rc); return rc; } memset(msg_pub, 0, MSG_PUB_MAX_SIZE); construct_report_process_info_shadow(msg_pub, type); rc = mqtt_client_publish(pclient, PUB_TYPE_SHADOW_OTA_INFO, msg_pub); if (rc < 0) { HAL_Free(msg_pub); OTA_LOG_EX(LOG_Error,"error occur when publish,rc:%d\r\n", rc); return rc; } HAL_Free(msg_pub); IOT_MQTT_Yield(pclient, 2000); } #endif #if (MQTT_SERVER == MQTT_SERVER_ALIBABA) if (pclient){ char *msg_pub; iotx_mqtt_topic_info_t topic_msg; OTA_LOG_EX(LOG_Info, "OTA Report %x\r\n", system_upgrade_flag_check()); msg_pub = HAL_Malloc(MSG_PUB_MAX_SIZE); memset(msg_pub, 0, MSG_PUB_MAX_SIZE); construct_report_process_info(&topic_msg, msg_pub); topic_msg.qos = IOTX_MQTT_QOS0; topic_msg.retain = 0; topic_msg.dup = 0; rc = IOT_MQTT_Publish(pclient, TOPIC_OTA_PROGRESS, &topic_msg); if (rc < 0) { HAL_Free(msg_pub); OTA_LOG_EX(LOG_Error,"error occur when publish,rc:%d\r\n", rc); return rc; } HAL_Free(msg_pub); IOT_MQTT_Yield(pclient, 1000); } #endif ne_cfg_set_init_type(type); /* restart system to enter ota mode */ system_restart(); return rc; } #if 1 #if (OTA_SERVER_TYPE == OTA_HTTP) //#define ota_payload "{\"cmdId\":2001,\"cryptoType\":0,\"timeStamp\":1526625689,\"msgContent\":{\"mcuOTAInfo\":{\"url\":\"https://www.163.com/mcu_ota_2.bin\",\"md5\":\"1209a15c8794da4301780c479a7835a6\",\"totalLength\":487156,\"segmentLength\":262144,\"otaMode\":0,\"ver\":3456},\"wifiOTAInfo\":{\"url\":\"http://10.240.84.20/user1.2048.new.5.bin\",\"md5\":\"Adca8a82934afde782f6b16b1d83e375\",\"totalLength\":470676,\"segmentLength\":470676,\"otaMode\":1,\"ver\":2}}}" #define ota_payload "{\"cmdId\":2001,\"cryptoType\":0,\"timeStamp\":1526625689,\"msgContent\":{\"wifiOTAInfo\":{\"url\":\"https://nim.nosdn.127.net/23056021/1/1/1529374349/\",\"md5\":\"Adca8a82934afde782f6b16b1d83e375\",\"totalLength\":470676,\"segmentLength\":470676,\"otaMode\":1,\"ver\":1232},\"mcuOTAInfo\":{\"url\":\"https://nim.nosdn.127.net/23056021/2/3/1529483539/\",\"md5\":\"4515be28c739e86328998bce84b2d05f\",\"totalLength\":452324,\"segmentLength\":226162,\"otaMode\":1,\"ver\":1234}}}" #else #define ota_payload "{\"cmdId\":2001,\"cryptoType\":0,\"timeStamp\":1526625689,\"msgContent\":{\"wifiOTAInfo\":{\"url\":\"https://nim.nosdn.127.net/23056021/1/1/1529374349/\",\"md5\":\"Adca8a82934afde782f6b16b1d83e375\",\"totalLength\":470676,\"segmentLength\":470676,\"otaMode\":1,\"ver\":1232},\"mcuOTAInfo\":{\"url\":\"https://nim.nosdn.127.net/23056021/1/1/1529374349/\",\"md5\":\"Adca8a82934afde782f6b16b1d83e375\",\"totalLength\":470676,\"segmentLength\":8192,\"otaMode\":1,\"ver\":1234}}}" #endif extern char __product_key[PRODUCT_KEY_LEN + 1]; extern char __device_name[DEVICE_NAME_LEN + 1]; void ota_mqtt_test_OTA_received(void *pclient) { iotx_mqtt_event_msg_pt pMsg; iotx_mqtt_topic_info_pt topic_info; char *topic, *payload; topic_info = HAL_Malloc(sizeof(iotx_mqtt_topic_info_t)); topic = HAL_Malloc(128); sprintf(topic, TOPIC_OTA_UPGRADE, __product_key, __device_name); topic_info->topic_len = strlen(topic); topic_info->ptopic = topic; payload = HAL_Malloc(512); sprintf(payload, ota_payload); topic_info->payload_len = strlen(payload); topic_info->payload = payload; pMsg = HAL_Malloc(sizeof(iotx_mqtt_event_msg_t)); pMsg->event_type = 0; pMsg->msg = topic_info; upgrade_message_arrive(NULL, pclient, pMsg); HAL_Free(topic); HAL_Free(payload); HAL_Free(topic_info); HAL_Free(pMsg); } #endif