文章目录
- 一、前言;
- 二、回顾下`OTA`的流程;
- 三、`lwip`使用网络框架的知识;
- 如何处理服务器返回的数据?
- 五、风扇区域的擦除和烧写?
- 六、如何调用?
- 好好享受吧!
- 八、下载:
- 九、工程截图:
一、前言;
esp8266
实现OTA
这不再是一件奇怪的事情,主要是esp8266
还支持裸跑非系统和裸跑非系统rtos
实时系统的划分导致现在有两个版本的代码,我已经预料到了NONOS
如何实现我们的固件的远程升级,所以这篇文章是基于freeRtos
实时系统。- 主要涉及以下知识:
lwip
基本使用网络框架、域名和DNS
使用分析。freeRtos
基本理解。http
协议的理解和原则。esp8266
对内存划分的理解。
- 实现的功能特征如下:
- 支持本地服务器和远程广域网服务器。
DNS
解析) - 支持显示下载固件的进度。
二、回顾下
OTA
的流程;- 我们已经很清楚了,
esp8266
实现远程升级的原理如下:
Created with Raphaël 2.2.0 开始要求服务器资源,比如user2.4096.new.6.bin 良好的网络,正确的服务器响应。准备下载... ... 下载后,确认是否重启? 重启成功!根据 boot user2.4096执行信息.new.6.bin。 yes
- 事实上,私人服务的放置也是如此。您只需在阿里巴巴云、京东云或腾讯云平台上建立服务器,放置编译固件并提供与当地相同的链接。
- 注意两个固件的配置信息必须一致。
三、
lwip
使用网络框架的知识;- ①:
lwip
使用DNS
对网站域名进行分析。
因为
esp8266
这种实时系统支持lwip
,所以可以直接调用;首先,打开它DNS
功能,在lwipopts.h
第一个文件有宏定义LWIP_DNS
把它放在一边。以下是调用代码:
//dns begindns_init(); ///初始化struct ip_addr addr;///你要分析的域名,比如 www.baidu.comchar name0[] = "www.baidu.com"; ///调用netcon_gethostbynameerr_t err = netconn_gethostbyname((char*) name0, &addr);///定义字符串接收IPcharr* 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.0Host: www.baidu.comConnection: keep-aliveCache-Control: no-cacheUser-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,sdchAccept-Language: zh-CN,zh;q=0.8
- ③:如果响应正确,我们也应该遵循一定的规则,我们可以使用它
postMan
看看工具,显示下载的大小是296412
,单位是Byte
;然后,让我们根据这个尺寸来判断我们的下载进度!
如何处理服务器返回的数据?
- 我们是采用
http
请求服务器,如果服务器响应正确,也会按照一定的协议返回给我们 。小徐尝试了几个服务器,一个是Tomcat
,一个是用图灵
构建的服务器返回的内容格式不同。最大的区别是:一是Accept-Length:
指定内容大小,一是指定内容,Content-Length:
指定尺寸,所以我的包装方法文件,可以完全兼容。详情请参见以下代码的一部分。
//判断 "Accept-Length" 或者 "Context-Length"bool isAccept = false;if((ptr = (char *)strstr(pusrdata, "Accept-Length")) != NULL)isAccept = true;elseisAccept = false;if (totallength == 0&& (ptr = (char *)strstr(pusrdata, "\r\n\r\n")) != NULL) {ptr = (char *) strstr(pusrdata, "\r\n\r\n");length -= ptr - pusrdata;length -= 4;printf("ota_start_download:upgrade file download pusrdata:%s\n\n",pusrdata);//返回"Accept-Length: iff首次出现在pusrdata中 (isAccept)ptr = (char *) strstr(pusrdata, "Accept-Length: ");elseptr = (char *) strstr(pusrdata, "Content-Length: ");if (ptr != NULL) { ///注意这个”Accept-Length" 或者 "Context-Length“不同的if占用不同的字节 (isAccept)ptr += 15;elseptr += 16;ptmp2 = (char *) strstr(ptr, "\r\n");if (ptmp2 != NULL) 打印下收到的数据头///printf("ptr = %s \n", ptr);///清空lengthbufermeteter(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, "\r\n\r\n");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\% \n", totallength, sumlength,projress);
五、风扇区域的擦除和烧写?
- 要弄明白
esp8266
的OTA
,我们必须清楚地了解它的风扇区域。我们现在最常用的是esp8266-12f
,它的容量大小是32Mbit
,也就是说,最大代码是1M
,2个分区:1024+1024
!而大家玩的esp8266-01
是8Mbit
, 也即是512+512
。 - 从下面官方给出的图片可以看出,一个支持
OTA
如果你的风扇分布是对称的,风扇区分布是对称的8Mbit
那么最大的支持就是512K,而且32Mbit
是最大的支持1M
,其他空间用户数据!说白了,只加载了这两个空间的任何固件!
六、如何调用?
- 因为个人小徐是一个喜欢包装代码以减少耦合的程序员,所以在做项目时应该明确划分功能。因此,这种包装也是基于面对象的想法。通过回调实现进度:
- 判断目前在哪个分区,如果是分区一,那么我们拉的是分区二的代码,反而是一样的;
//注册回调函数system_ota_register_callBack(ota_call_back);//如果现在是这样 user1.bin,然后拉云 user2.bin 升级if(system_upgrade_userbin_check()==UPGRADE_FW_BIN1){printf("now is user1.bin\n");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.\n");system_ota_config_start("www.baidu.com","api/v1/emq/download/iot?bin1.bin",80,true,true);}else{ //不支持云升级printf(”now is not support FOTA..\n");}
- 看看我们刚刚注册的回调函数:
void ota_call_back(int progress, ota_code code) { ///下载时可以看到进度值。if (code == OTA_DOWNLOADING) {printf("ota_DowmLoading: %d%% \n",progress);///下载完毕} else if (code == OTA_SUCCEED) {printf("OTA_SUCCEED! \n");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是状态码typedeffe 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
有趣的问题是来回升级,在两个分区跳跃。。哈哈。
九、工程截图:
基于freertos,乐信esp8266实现了私有服务器本地远程OTA升级