zoukankan      html  css  js  c++  java
  • 乐鑫esp8266基于freeRtos实现私有服务器本地远程OTA升级

    代码地址如下:
    http://www.demodashi.com/demo/13533.html

    一、前言;


    • esp8266实现OTA已经不是什么奇怪的事情了,主要esp8266还有支持裸跑非系统和rtos实时系统之分,导致现在有2个版本的代码,前面我已经料到了在NONOS怎么实现远程升级我们的固件,那么这篇的话,而是基于freeRtos实时系统的。
    • 主要涉及到知识有以下:
      • lwip网络框架的基本使用,域名和DNS解析的使用。
      • freeRtos的基本认识。
      • http请求的协议认识和原理。
      • esp8266的内存划分的认识。
    • 实现的功能特色有以下:
      • 支持本地服务器和远程广域网服务器。(通过DNS解析)
      • 支持显示下载固件进度。

    二、回顾下OTA的流程;


    • 我们已经很清楚,esp8266实现远程升级的原理过程如下:
    st=>start: 开始请求服务器资源比如:user2.4096.new.6.bin
    e=>end: 重启成功!根据 boot 信息,执行user2.4096.new.6.bin。
    op=>operation: 网络良好,服务器响应正确。准备下载... ...
    cond=>condition: 下载完毕,确认是否重启?
    
    st->op->cond
    cond(yes)->e
    

    • 私有服务的放置其实也是一样,只需要在阿里云或者京东云或者腾讯云这些平台搭建服务器,把编译好的固件放上去,提供链接即可,和本地一样。

    • 注意放上去的2个固件的配置信息必须一致。


    三、lwip网络框架的知识的使用;


    • ①:lwip使用DNS来解析网址域名。

    因为esp8266这个实时系统支持lwip,所以直接调用即可;首先要打开DNS功能,在lwipopts.h头文件有个宏定义LWIP_DNS置为1即可。下面就是调用代码了:

            //dns begin
    		dns_init(); //初始化
    		struct ip_addr addr;
    		//你要解析的域名,比如 www.baidu.com
    		char name0[] = "www.baidu.com"; 
    		//调用netconn_gethostbyname
    		err_t err = netconn_gethostbyname((char*) name0, &addr);
    		//定义字符串来接收IP
    		char* ipAdress;
    		if (err == ERR_OK) {
    		    //格式化IP
    		    ipAdress = ip_ntoa(&addr);
    		    //打印下
    			printf("dns ok , the %s ip is:%s",name0, ipAdress);
    		}else{
    			return;
    		}
    		//dns over
    

    • ②:先通过socket连接服务器的端口,一般地,默认是80!其中http请求的格式内容如下:注意这个Host的内容可以是域名或者IP,这个要根据服务器来配置。
    GET /api/download/user2.4096.new.6.bin HTTP/1.0
    Host: www.baidu.com
    Connection: keep-alive
    Cache-Control: no-cache
    User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36 
    Accept: */*
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-CN,zh;q=0.8
    

    • ③:如果正确响应的话,也是要遵循一定的规则的,我们可以用postMan工具来看看,这显示下载的大小是296412,单位是Byte;那么下面的话,我们根据这个大小去判断我们下载的进度啦!

    四、如何处理服务器返回的数据?


    • 我们是采用http请求服务器,那么服务器响应正确的话,也是按照一定的协议返回来给我们的 。小徐在尝试了几个服务器,一个是Tomcat,一个是用php搭建的服务器,返回的内容格式有所差别,最大的不同点是:一个是Accept-Length:指定内容大小,一个是Content-Length:指定大小,为此我这次封装的方法文件,是完全可以兼容起来的。详情可以看下面的部分代码。
        //判断 "Accept-Length" 或者 "Context-Length"
    	bool isAccept = false;
    	if((ptr = (char *)strstr(pusrdata, "Accept-Length")) != NULL)
    		isAccept = true;
    	else
    		isAccept = false;
    
    	if (totallength == 0&& (ptr = (char *)strstr(pusrdata, "
    
    ")) != NULL) {
    
    		ptr = (char *) strstr(pusrdata, "
    
    ");
    		length -= ptr - pusrdata;
    		length -= 4;
    
    		printf("ota_start_download:upgrade file download pusrdata:%s
    
    ",
    				pusrdata);
    
    		//返回"Accept-Length: "在pusrdata中首次出现的地址
    		if (isAccept)
    			ptr = (char *) strstr(pusrdata, "Accept-Length: ");
    		else
    			ptr = (char *) strstr(pusrdata, "Content-Length: ");
    
    		if (ptr != NULL) {
    		
                //注意这个"Accept-Length" 或者 "Context-Length"占用的字节不同的
    			if (isAccept)
    				ptr += 15;
    			else
    				ptr += 16;
    
    			ptmp2 = (char *) strstr(ptr, "
    ");
    
    			if (ptmp2 != NULL) {
    
    				//打印下收到的数据头
    				//printf("ptr = %s 
    ", ptr);
    
    				//清空lengthbuffer
    				memset(lengthbuffer, 0, sizeof(lengthbuffer));
    				memcpy(lengthbuffer, ptr, ptmp2 - ptr);
    				sumlength = atoi(lengthbuffer);
    
    				if (sumlength > 0) {
    					if (false == system_upgrade(pusrdata, sumlength)) {
    						system_upgrade_flag_set(UPGRADE_FLAG_IDLE);
    						goto ota_recycle;
    					}
    					flash_erased = true;
    					ptr = (char *) strstr(pusrdata, "
    
    ");
    					if (false == system_upgrade(ptr + 4, length)) {
    						system_upgrade_flag_set(UPGRADE_FLAG_IDLE);
    						goto ota_recycle;
    					}
    					totallength += length;
    					return;
    				}
    
    			} else {
    				system_upgrade_flag_set(UPGRADE_FLAG_IDLE);
    				goto ota_recycle;
    			}
    
    		}
    

    • 上面我们拿到了这个总数据,那么我们下载固件时候又是怎么样判断是否拉取全部呢?其实通过我们拉取的内容大小和这个总数相比较,就可以得到是否下载完毕。

    一般我们都是下载的数据量除于总数据量就是百分比了!由于esp8266不支持浮点类型,也就是不支持小数。 但是我们又要显示进度咋办?yiban其实很简单,算法如下:

    • sumlength为总数据量,totallength 是已下载的数据量!
    int part =sumlength/100;
    int projress = (totallength / part);
    printf("已经下载: %d , 总数据: %d , 进度: %d\% 
    ", totallength, sumlength,projress);
    

    五、扇区的擦除和烧写?


    • 要弄明白esp8266OTA,必须要对其的扇区明明白白。我们现在最常用的是esp8266-12f,其的容量大小是32Mbit,也就是最大代码是1M,2个分区:1024+1024!而大家玩的esp8266-018Mbit, 也即是512+512

    • 从下面官方给的图可以看到,一个支持OTA的扇区分布是对称的,如果你的是8Mbit的话,那么最大的支持就是512K,而32Mbit的就是最大支持1M,其他空间用户数据!说白了,加载的只是这2个空间的任意一个固件!


    六、如何调用?


    • 因为个人小徐是个喜欢封装代码降低耦合的程序员,做项目都要明确划分功能。那么这次的封装也是基于面对对象思想做的,用回调实现进度显示:

    • 判断当前是处于哪个分区,如果是分区一那么我们拉取的就是分区二的代码,反而也是这样;
         //注册回调函数
    	system_ota_register_callBack(ota_call_back);
    	//如果当前是处于 user1.bin,那么就拉取云端 user2.bin 升级
    	if(system_upgrade_userbin_check()==UPGRADE_FW_BIN1){
    		printf("now is user1.bin
    ");
    		system_ota_config_start("www.baidu.com","api/v1/emq/download/iot?bin2.bin",80,true,true);
    	//如果当前是处于 user2.bin,那么就拉取云端 user1.bin 升级
    	}else if(system_upgrade_userbin_check()==UPGRADE_FW_BIN2){
    		printf("now is user2.bin.
    ");
    		system_ota_config_start("www.baidu.com","api/v1/emq/download/iot?bin1.bin",80,true,true);
    	}else{
    	    //不支持云端升级
    		printf("now is not support FOTA..
    ");
    	}
    

    • 看看我们刚刚注册的那个回调函数:
    void ota_call_back(int progress, ota_code code) {
    
        //下载中,可看到进度数值。
    	if (code == OTA_DOWNLOADING) {
    		printf("ota_DowmLoading: %d%% 
    ",progress);
    	//下载完毕
    	} else if (code == OTA_SUCCEED) {
    		printf("OTA_SUCCEED! 
    ");
    		system_upgrade_deinit();
    		// 看你需要,下载完毕是否重启系统
    		system_upgrade_reboot();
    	}
    }
    

    • 这是小徐专心封装的函数和方法,直接调用即可:
    typedef enum {
    	OTA_FAIL = 0,    //下载失败,可能是网速问题
    	OTA_SUCCEED ,  //下载并且烧录成功,请重启系统
    	OTA_DOWNLOADING ,  //下载中,可看进度条
    	OTA_INIT_ERROR ,	 //初始化失败,网址有误。
    	OTA_INIT_OK ,	 //初始化成功。
    }ota_code;
    
    //progress是进度条,code是状态码
    typedef void (*ota_download_CallBack)(int progress,ota_code code);
    
    //注册下载服务器bin文件的进度百分比回调函数:0到100% (整型);
    void system_ota_register_callBack(ota_download_CallBack callBack);
    
    /******************************************************************************
     * FunctionName : system_ota_config_start
     * Description  : 传入网址,并且开始下载
     * Parameters   : char *domainName : 域名或ip地址
     *                char* requestResource : 请求的资源地址
     *                int port         : 端口号
     *                bool isDNS       : 是否要DNS解析
     *                bool isGet       : get请求或者post提交
     * Returns      : 是否成功
     *******************************************************************************/
    bool system_ota_config_start(char *domainName, char* requestResource,int port,
    		bool isDNS, bool isGet);
    

    七、好好享用吧!


    • 下面是我串口打印截图:



    八、下载:


    • 这个demo需要注意的是:如果你的服务器地址直接是IP的,就不用DNS解析;如果是域名,那么就需要DNS的;还有需要啰嗦的一点就是:注意你的网络良好、服务器是否访问、还有最为重要的分区说明。

    • 好了,如果你确保上面问题,那么这个demo发生有趣的问题,就是不断来回升级,在两个分区跳转。。呵呵。


    九、工程截图:


    乐鑫esp8266基于freeRtos实现私有服务器本地远程OTA升级

    代码地址如下:
    http://www.demodashi.com/demo/13533.html

    注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权

  • 相关阅读:
    webdav srs相关
    How To Configure WebDAV Access with Apache on Ubuntu 14.04
    ubuntu 编译lighttpd
    srs编译及推流测试
    Compile pciutils (lspci, setpci) in Windows x86,在 Windows x86 平台下编译 pciutils (lspci, setpci)
    mingw MSYS2 区别
    Qt之美(三):隐式共享
    Qt之美(二):元对象
    Qt之美(一):d指针/p指针详解
    C++的栈空间和堆空间
  • 原文地址:https://www.cnblogs.com/demodashi/p/9443503.html
Copyright © 2011-2022 走看看