/********************************测试例程说明************************************ * 功能说明: * 通过HTTP协议,先获取服务器上最新的固件版本信息(版本信息中一般包含有版本号、固件下载地址URL等信息), * 通过获取到的最新版本号和当前系统版本进行比较,如果已经是最新版本则退出升级,否则提取最新固件URL,获取最新固件进行升级。 * 通过该方法可以实现设备开机版本自动检测、批量设备自检升级。 */ #include "system/includes.h" #include "os/os_api.h" #include "http/http_cli.h" #include "fs/fs.h" #include #include "storage_device.h" #include "net_update.h" #include "app_config.h" #include "wifi/wifi_connect.h" #include "http/http_cli.h" #include "json_c/json_object.h" #include "json_c/json_tokener.h" #include "syscfg_id.h" #ifdef USE_HTTP_UPGRADE_DEMO2 /** 固件版本信息URL*/ #define OTA_VERSION_URL "https://profile.jieliapp.com/license/v1/fileupdate/check2?auth_key=Jk0D2fufdROhKX6q&proj_code=jieli_wl80_test" #define DEFAULT_RECV_BUF_SIZE (4*1024) //数据缓存BUF大小 #define MAX_RECONNECT_CNT 10 //最大重连次数 struct download_hdl { char *url; //下载链接 u32 file_size; //需要下载的文件大小 u8 *recv_buf; //数据缓存BUF u32 recv_buf_size; //数据缓存BUF大小 u32 download_len; //已下载的数据长度 u32 reconnect_cnt; //重连次数 httpcli_ctx ctx; //http请求参数 const struct net_download_ops *download_ops; //http操作集 }; struct download_parm { char *url; //下载链接 }; enum { OTA_NO_ERROR = 0, OTA_GET_SAME_VERSION = -80, OTA_GET_VERSION_FAILURE = -81, }; //设备版本描述信息长度 #define VERSION_INTRO_LEN 64 //设备版本号长度 #define VERSION_LEN 16 /* 设备当前版本号 */ static char g_currentVersion[VERSION_LEN] = "v1.0.0-001"; //应当填写烧录时的版本号 /* 保存从服务器上获取到的最新版本信息 */ static char g_latestVersion[VERSION_LEN] = {0}; /** 保存固件升级的URL*/ #define OTA_URL_LEN 512 static char http_ota_url[OTA_URL_LEN] = {0}; /** 获取版本信息接收buf大小*/ #define HTTP_RECV_BUF_SIZE 1 * 1024 //需要在sysfg_id.h配置CFG_UPDATE_VERSION_INFO //开机时获取固件版本号 static void get_fw_version(void) { int ret; ret = syscfg_read(CFG_UPDATE_VERSION_INFO, (u8 *)g_currentVersion, VERSION_LEN); if (ret < 0) { //读取版本失败,使用默认版本号 ret = syscfg_write(CFG_UPDATE_VERSION_INFO, (u8 *)g_currentVersion, VERSION_LEN); if (ret < 0) { printf("write version error!"); } else { printf("No version number, initial version number(%s)\n", g_currentVersion); } } else { puts("\n*************************************************\n"); printf(" *Current Firmware Version (%s)* \n", g_currentVersion); puts("\n*************************************************\n"); } } /** 升级成功后更新固件版本号*/ static void renew_fw_version(void) { int ret; ret = syscfg_write(CFG_UPDATE_VERSION_INFO, (u8 *)g_latestVersion, VERSION_LEN); if (ret < 0) { printf("renew_fw_version err!/n"); } } static int get_current_version(void) { /* 获取设备当前版本号到全局变量g_currentVersion */ get_fw_version(); return 0; } static int get_latest_version_info(char *latestVersion, int latestVersionLen, char *latestIntro, int latestIntroLen) { int ret = 0; int err = -1; http_body_obj http_body_buf; json_object *parse = NULL, *data = NULL; httpcli_ctx *ctx = (httpcli_ctx *)calloc(1, sizeof(httpcli_ctx)); if (!ctx) { printf("calloc faile\n"); return err; } memset(&http_body_buf, 0x0, sizeof(http_body_obj)); http_body_buf.recv_len = 0; http_body_buf.buf_len = HTTP_RECV_BUF_SIZE; http_body_buf.buf_count = 1; http_body_buf.p = (char *) malloc(http_body_buf.buf_len * http_body_buf.buf_count); if (!http_body_buf.p) { free(ctx); return err; } ctx->url = OTA_VERSION_URL; ctx->timeout_millsec = 5000; ctx->priv = &http_body_buf; ctx->connection = "close"; ret = httpcli_get(ctx); if (ret != HERROR_OK) { printf("get_latest_version_info : httpcli_get fail\n"); goto EXIT; } else { if (http_body_buf.recv_len > 0) { /** 对从服务器收到的json格式的版本信息进行解析,获取其版本号,版本描述信息和升级固件的URL信息*/ parse = json_tokener_parse(http_body_buf.p); if (200 == json_object_get_int(json_object_object_get(parse, "code"))) { data = json_object_object_get(parse, "data"); if (data != NULL) { /** 获取版本信息*/ strcpy(latestVersion, json_object_get_string(json_object_object_get(data, "version"))); /** 保存从服务器上获取的版本号*/ strcpy(g_latestVersion, latestVersion); /** 获取版本描述信息*/ strcpy(latestIntro, json_object_get_string(json_object_object_get(data, "explain"))); /** 获取升级文件URL*/ strcpy(http_ota_url, json_object_get_string(json_object_object_get(data, "url"))); puts("\n*************************************************************\n"); printf("latestVersion:%s\n", latestVersion); printf("explain:%s\n", latestIntro); printf("update url:%s\n", http_ota_url); puts("\n*************************************************************\n"); /** 获取成功*/ err = 0; } } } } EXIT: httpcli_close(ctx); if (http_body_buf.p) { free(http_body_buf.p); http_body_buf.p = NULL; } if (ctx) { free(ctx); ctx = NULL; } if (parse) { json_object_put(parse); parse = NULL; } return err; } //获取服务器上最新版本号 int http_ota_get_version(char *version, int *verLen) { if (version == NULL || verLen == NULL) { return OTA_GET_VERSION_FAILURE; } char latestVersion[VERSION_LEN] = {0}; char latestVerIntro[VERSION_INTRO_LEN] = {0}; if (get_latest_version_info(latestVersion, VERSION_LEN, latestVerIntro, VERSION_INTRO_LEN) != 0) { return OTA_GET_VERSION_FAILURE; } if (get_current_version() != 0) { return OTA_GET_VERSION_FAILURE; } if (strlen(g_currentVersion) == strlen(latestVersion) && memcmp(g_currentVersion, latestVersion, strlen(latestVersion)) == 0) { return OTA_GET_SAME_VERSION; } else { if (version == NULL) { return OTA_GET_VERSION_FAILURE; } memcpy(version, latestVersion, strlen(latestVersion)); *verLen = strlen(latestVersion); } return OTA_NO_ERROR; } //http回调函数 static int http_user_cb(void *ctx, void *buf, unsigned int size, void *priv, httpin_status status) { struct download_hdl *hdl = (struct download_hdl *)priv; if (status == HTTPIN_HEADER) { //第一次连接时,记录文件总长度 if (hdl->ctx.lowRange == 0) { hdl->file_size = hdl->ctx.content_length; } log_e("http_need_download_file=%d KB", hdl->file_size / 1024); } return 0; } static void download_task(void *priv) { struct download_hdl *hdl = priv; void *update_fd = NULL; int ret = 0; int err = 0; char sock_err = 0; update_fd = net_fopen(CONFIG_UPGRADE_OTA_FILE_NAME, "w"); if (!update_fd) { log_e("open update_fd error\n"); goto _out_; } _reconnect_: //发起连接请求,建立socket连接 ret = hdl->download_ops->init(&hdl->ctx); if (ret != HERROR_OK) { if (hdl->ctx.req_exit_flag == 0) { if (hdl->reconnect_cnt < MAX_RECONNECT_CNT) { hdl->reconnect_cnt++; goto _reconnect_; } else { log_e("download reconnect upto max count.\n"); goto _out_; } } } while (hdl->ctx.req_exit_flag == 0) { ret = hdl->download_ops->read(&hdl->ctx, (char *)hdl->recv_buf, hdl->recv_buf_size);//最大接收为recv_buf_size if (ret < 0) { //读取数据失败 sock_err = 1; goto _out_; } else { hdl->download_len += ret; err = net_fwrite(update_fd, hdl->recv_buf, ret, 0); if (err != ret) { goto _out_; } } if (hdl->download_len >= hdl->file_size) { goto _finish_; } } _finish_: printf("download success.\n"); //更新版本号 renew_fw_version(); net_fclose(update_fd, sock_err); system_reset(); _out_: //关闭网络连接 hdl->download_ops->close(&hdl->ctx); net_fclose(update_fd, sock_err); free(hdl->url); free(hdl->recv_buf); memset(hdl, 0, sizeof(struct download_hdl)); free(hdl); } static int http_create_download_task(void) { struct download_parm parm = {0}; struct download_hdl *hdl = NULL; parm.url = http_ota_url; //申请堆内存 hdl = (struct download_hdl *)calloc(1, sizeof(struct download_hdl)); if (hdl == NULL) { return -ENOMEM; } hdl->download_ops = &http_ops; hdl->recv_buf_size = DEFAULT_RECV_BUF_SIZE; hdl->url = (char *)calloc(1, strlen(parm.url) + 1); if (hdl->url == NULL) { return -ENOMEM; } hdl->recv_buf = (u8 *)calloc(1, hdl->recv_buf_size); if (hdl->recv_buf == NULL) { return -ENOMEM; } strcpy(hdl->url, parm.url); //http请求参数赋值 hdl->ctx.url = hdl->url; hdl->ctx.cb = http_user_cb; hdl->ctx.priv = hdl; hdl->ctx.connection = "close"; //创建下载线程 return thread_fork("download_task", 20, 2 * 1024, 0, NULL, download_task, hdl); } static void http_upgrade_start(void *priv) { enum wifi_sta_connect_state state; while (1) { printf("Connecting to the network...\n"); state = wifi_get_sta_connect_state(); if (WIFI_STA_NETWORK_STACK_DHCP_SUCC == state) { printf("Network connection is successful!\n"); break; } os_time_dly(1000); } int ret; char latestVersion[VERSION_LEN] = {0}; /* 保存获取的最新版本号 */ int verLen = 0; ret = http_ota_get_version(latestVersion, &verLen); if (ret == OTA_GET_VERSION_FAILURE) { printf("http_ota_get_version err!"); return; } if (ret == OTA_GET_SAME_VERSION) { printf("The current version is the latest version!"); return; } printf("latest version :[%s]\n", latestVersion); http_create_download_task(); } //应用程序入口,需要运行在STA模式下 void c_main(void *priv) { if (thread_fork("http_upgrade_start", 10, 4 * 1024, 0, NULL, http_upgrade_start, NULL) != OS_NO_ERR) { printf("thread fork fail\n"); } } late_initcall(c_main); #endif //USE_HTTP_UPGRADE_DEMO2