4.wifi开发【网络编程2】WiFi HTTP Client编程,WiFi HTTP Server编程,Smartconfig,SNTP通信协议,FOTA远程固件升级

一。HTTP协议

1.HTTP介绍

(1)什么是超文本HyperText?

        包含有超链接(Link)和各种多媒体元素标记(Markup)的文本。这些超文本文件彼此链接,形成网状(Web),因此又被称为网页(Web Page)。这些链接使用URL表示。最常见的超文本格式是超文本标记语言HTML。

(2)什么是URL?

        URL即统一资源定位符(Uniform Resource Locator),用来唯一地标识万维网中的某一个文档。URL由协议、主机和端口(默认为80)以及文件名三部分构成。

如:http://www.makeru.com.cn//course/3172.html

(3)什么是超文本传输协议HTTP?

        是一种按照URL指示,将超文本文档从一台主机(Web服务器)传输到另一台主机(浏览器)的应用层协议,以实现超链接的功能。

2.HTTP工作原理(请求/响应交互模型)

在用户点击URL为http://www.makeru.com.cn//course/3172.html 的链接后,浏览器和Web服务器执行以下动作:

1.浏览器分析超链接中的URL

2.浏览器向DNS请求解析www.makeru.com.cn的IP地址

3.DNS将解析出的IP地址202.2.16.21返回浏览器

4.浏览器与服务器建立TCP连接(80端口)

5.浏览器请求文档:GET /index.html

6.服务器给出响应,将文档 index.html发送给浏览器

7.释放TCP连接

8.浏览器显示index.html中的内容

3.HTTP报文结构

请求报文

即从客户端(浏览器)向Web服务器发送的请求报文。报文的所有字段都是ASCII码。

响应报文

即从Web服务器到客户机(浏览器)的应答。报文的所有字段都是ASCII码。

补充:请求报文的方法/对函数的解释

方法(Method)是对所请求对象所进行的操作,也就是一些命令。请求报文中的操作有:

函数解释:
(1)GET

        GET请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),以?分割URL和传输数据,参数之间以&相连,如:login.action?name=sean&password=123。如果数据是英文字母/数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密,得出如:%E4%BD%A0%E5%A5%BD,其中%XX中的XX为该符号以16进制表示的ASCII。

(2)POST

        1.POST把提交的数据则放置在是HTTP包的包体中。

        2.POST的安全性要比GET的安全性高。注意:这里所说的安全性和上面GET提到的“安全”不是同个概念。上面“安全”的含义仅仅是不作数据修改,而这里安全的含义是真正的Security的含义,比如:通过GET提交数据,用户名和密码将明文出现在URL上,因为(1)登录页面有可能被浏览器缓存,(2)其他人查看浏览器的历史纪录,那么别人就可以拿到你的账号和密码了,除此之外,使用GET提交数据还可能会造成Cross-site request forgery攻击。

        3.总结一下,Get是向服务器发索取数据的一种请求,而Post是向服务器提交数据的一种请求,在FORM(表单)中,Method默认为"GET",实质上,GET和POST只是发送机制不同,并不是一个取一个发!

实验1:使用commbox模拟访问百度

1.建立一个socket客户端与服务器

2.进入浏览器(点击F12进入开发者的调试模式下)

3.socket客户端:目标ip设置为百度的IP:39.156.66.18(从上个图片得到),端口设置为80

GET /index.html HTTP/1.1
Host: www.baidu.com

注意:发送需要两个回车,因为GET数据报结束要两个回车(看上面数据报格式)

4.socket服务器:

1.socket服务器开始运行(复制那个socket客户端申请到的数据)

2.在页面中加载127.0.0.1

3.socket服务器发送数据(刚刚复制过来的数据)

        页面就会出现东西,出现了就代表成功了。

实验2:wifi访问百度首页

(1)实验要求

模拟http协议,访问百度首页www.baidu..com/index.html 串口打印出百度反回数据

(2)功能实现

新建httpclient工程目录

1.在SDK目录下新建httpclient目录

2.拷贝tcpclient目录下所有文件到httpclient目录下

新建httpclient源码文件

1.在user目录下新建t httpclient.c

2.在include目录下新建httpclient.h

Sourceinsight配置

在之前的工程中,移除tcpcserver(就是上一节最后一个)文件夹

添加httpclient文件夹

同步文件

(3)代码实现

1.复制之前tcpclient代码到httpclient上,进行修改

    (1)重命名TcpClient_init为HttpClient_init

#ifndef __HTTPCLIENT_H__
#define __HTTPCLIENT_H__

#ifdef __cplusplus
extern "C" {
#endif

void ATaskHttpClient( void *pvParameters );
void HttpClient_init(void);

#ifdef __cplusplus
}
#endif

#endif

    (2)重命名ATaskTcpClient为ATaskHttpClient

#include "esp_common.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"
#include "httpclient.h"

#define SERVERADDR "39.156.66.18"
#define SERVERPORT 8000
const char GetStr[] = "GET /index.html HTTP/1.1\r\n"
"Host: www.baidu.com\r\n"
"\r\n";

#define HTTP_RCV_LEN 50000
/******************************************************************************
 * FunctionName : ATaskHttpClient
 * Description  : ATaskHttpClient 任务
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
void ATaskHttpClient( void *pvParameters )
{
    int iVariableExample = 0;
    int fd = -1;

    int NetTimeOnt = 20000;
    int ret;
    int i;
    struct hostent *phostent;
    struct sockaddr_in ServerAddr;
    char *Httpmsg;
    char *ipaddr = NULL;

    STATION_STATUS StaStatus;
    do
    {
        StaStatus = wifi_station_get_connect_status();
        vTaskDelay(100);
    }while(StaStatus != STATION_GOT_IP);
    
    fd = socket(PF_INET,SOCK_STREAM,0);
    if(fd == -1)
    {
        printf("get socket fail!\n");
        vTaskDelete(NULL);
        return;
    }
    setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,&NetTimeOnt,sizeof(int));
    phostent = gethostbyname("www.baidu.com");
    if(phostent == NULL)
    {
        printf("get host ip fail!\n");
        vTaskDelete(NULL);
        return;
    }
    else
    {
        for(i = 0;phostent->h_addr_list[i];i++)
        {
              ipaddr = inet_ntoa(*(struct in_addr*)(phostent->h_addr_list[i]));
               if(ipaddr != NULL)
               {
                    printf("host addr is:%s\n",  ipaddr );
                    break;
               }
        }
        if(ipaddr == NULL)
        {
            printf("error:get ip fail!\r\n");
            vTaskDelete(NULL);
            return;
        }
    }    
    memset(&ServerAddr,0,sizeof(ServerAddr));
    ServerAddr.sin_family = AF_INET;
    ServerAddr.sin_addr.s_addr = inet_addr(ipaddr);
    ServerAddr.sin_port = htons(80);
    ServerAddr.sin_len = sizeof(ServerAddr);

    do
        {
            ret = connect(fd,(struct sockaddr*)&ServerAddr,ServerAddr.sin_len);
            if(ret != 0)
            {
                printf("connect is fail!\n");
                vTaskDelay(100);
            }
        }
    while (ret != 0);
    
    Httpmsg = zalloc(HTTP_RCV_LEN);
	send(fd,GetStr,strlen(GetStr),0);
    for(;;)
    {
        do
         {
            ret = recv(fd,Httpmsg,50000,0);
            if(ret > 0)
            {
				//printf("ret=%d\n",ret);
                printf("%s",Httpmsg);
            }
            else
            {
                printf("HttpServer data is no!\n");
            }
         }while(ret == -1);       
    }
	free(Httpmsg);
    vTaskDelete( NULL );
}

/******************************************************************************
 * FunctionName : HttpClient_init
 * Description  : HttpClient_init 初始化
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
void HttpClient_init(void)
{
    xTaskCreate(ATaskHttpClient, "HttpClient", 256, NULL, 4, NULL);
}

2.修改user_main.c

    (1)包涵httpclient.h

    (2)在user_init里中添加HttpClient_init

代码详细解释:

1.判断是否获取到IP地址

2.创建socket

3.设置接收超时时间

4.获取域名IP

5.赋值server信息

6.连接到server端

7.发送数据到server端

8.从server端接收数据

结果:

1.连接上指定的wifi(4G),百度发回许多东西

二。Http Server编程

1.HTML介绍

(1)超文本标记语言 ,是用来描述网页的一种语言。

(2)HyperText Markup Language 不是一种编程语言。

(3)使用标记标签来描述网页 HTML,HTML 文档包含了HTML 标签及文本内容。

(4)HTML文档也叫做 web 页面。

一个例子:

1.HTML 标签是由尖括号包围的关键词,比如 <html>。

2.HTML 标签通常是成对出现的,比如 <body> 和 </body>。

3.标签对中的第一个标签是开始标签,第二个标签是结束标签。

4.开始和结束标签也被称为开放标签和闭合标签。

5.标签是用来描述内容如何显示在页面浏览器访问者是看不到标签的。

HTML元素

一组HTML标签将一段内容含其中之后,这个标签与文字被称为一个元素

1.应用

        在所有的HTML文件,最外层的元素是有HTML标签建立的,在HTML标签内,包含了两个直接的子元素,这两个标签是有head与body标签所构成的。head提供HTML文件的配置信息,body提供页面内容主体。

实验:

1.功能分析

1.模拟httpServer等待网页访问。

2.PC访问网址,查看网页信息。

2.功能实现

新建httpserver工程目录

1.在SDK目录下新建httpserver目录

2.拷贝tcpserver目录下所有文件到httpserver目录下

新建httpserver源码文件

1.在user目录下新建httpserver.c

2.在include目录下新建httpserver.h

Sourceinsight配置

1.在之前的工程中,移除tcpserver文件夹

2.添加httpserver文件夹

注意:还有同步

3.代码实现流程

(1)httpserver.h

#ifndef __HTTPSERVER_H__
#define __HTTPSERVER_H__

#ifdef __cplusplus
extern "C" {
#endif

void ATaskHttpServer( void *pvParameters );
void HttpServer_init(void);

#ifdef __cplusplus
}
#endif

#endif

(2)httpserver.c

#include "esp_common.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"
#include "httpserver.h"

#define SERVERADDR "192.168.31.158"
#define SERVERPORT 80

const char *DefaultPage=
"<html>"
"<head>"
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"
"<title>欢迎进入html世界</title>"
"</head>"
"<body>"
"<P>这会是一个很有趣的实验</P>"
"</body>"
"</html>";

/*
"<html>"
"<head><title>Default</title></head>"
"<body>I am Http server!</body>"
"</html>"
;
*/

// 发送200 ok报头
int file_ok(int cfd, long flen)
{
    char *send_buf = zalloc(sizeof(char)*100);
    sprintf(send_buf, "HTTP/1.1 200 OK\r\n");
    send(cfd, send_buf, strlen(send_buf), 0);
    sprintf(send_buf, "Connection: keep-alive\r\n");
    send(cfd, send_buf, strlen(send_buf), 0);
    sprintf(send_buf, "Content-Length: %ld\r\n", flen);
    send(cfd, send_buf, strlen(send_buf), 0);
    sprintf(send_buf, "Content-Type: text/html\r\n");
    send(cfd, send_buf, strlen(send_buf), 0);
    sprintf(send_buf, "\r\n");
    send(cfd, send_buf, strlen(send_buf), 0);
    free(send_buf);
    return 0;
}
/******************************************************************************
 * FunctionName : ATaskHttpServer
 * Description  : ATaskHttpServer 任务
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
void ATaskHttpServer( void *pvParameters )
{
    int iVariableExample = 0;
    int fd = -1;
    int cfd = -1;
    int NetTimeOnt = 2000;
    int ret;

    struct sockaddr_in ServerAddr;
    struct sockaddr ClientAddr;
    socklen_t ClientAddrlen = sizeof(struct sockaddr);
    char *Httpmsg;
    char *Sendmsg;

    STATION_STATUS StaStatus;
    do
    {
        StaStatus = wifi_station_get_connect_status();
        vTaskDelay(100);
    }while(StaStatus != STATION_GOT_IP);

    fd = socket(PF_INET,SOCK_STREAM,0);
    if(fd == -1)
    {
        printf("get socket fail!\n");
        vTaskDelete(NULL);
        return;
    }

    setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,&NetTimeOnt,sizeof(int));

    memset(&ServerAddr,0,sizeof(ServerAddr));
    ServerAddr.sin_family = AF_INET;
    ServerAddr.sin_addr.s_addr = INADDR_ANY;
    ServerAddr.sin_port = htons(SERVERPORT);
    ServerAddr.sin_len = sizeof(ServerAddr);

    if(bind(fd,(struct sockaddr*)&ServerAddr,ServerAddr.sin_len) != 0)
    {
            printf("bind socket fail!\n");
        vTaskDelete(NULL);
        return;
    }

    if(listen(fd,5) != 0)
    {
        printf("listen socket fail!\n");
        vTaskDelete(NULL);
        return;
    }
    Httpmsg = (char*)zalloc(sizeof(char)*1000);
    for(;;)
    {        
        cfd = accept(fd,&ClientAddr,&ClientAddrlen);
        if(cfd != -1)
        {           
            printf("HttpClient accept\n");
            ret = recv(cfd,Httpmsg,1000,0);
            if(ret > 0)
            {
                printf("HttpClient recv\n");
                printf("%s\n",Httpmsg);
                file_ok(cfd,strlen(DefaultPage));
                send(cfd,DefaultPage,strlen(DefaultPage),0);
            }
            else
            {
                printf("HttpClient data is no!\n");
            }
        }
        close(cfd);
    }
    free(Httpmsg);
    vTaskDelete( NULL );

}
/******************************************************************************
 * FunctionName : HttpServer_init
 * Description  : HttpServer_init 初始化
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
void HttpServer_init(void)
{
   xTaskCreate(ATaskHttpServer, "HttpServer", 256, NULL, 4, NULL);
}

1.判断是否获取到IP地址

2.创建socket

3.设置接收超时时间

4.赋值server信息

5.绑定socket

6.监听socket

7.处理Client 连接

8.接收Client 数据

9.发送数据到client

10.关闭socket

结果:

三。Smartconfig编程

1.Smartconfig

(1)使用领域

        Wi-Fi SmartConfig是一种简化设备连接到Wi-Fi网络的方法。它通常用于物联网(IoT)设备,包括智能家居设备、智能电器和可穿戴设备。

(2)功能

        使用Wi-Fi SmartConfig,用户可以通过移动应用程序或其他设备发送一个特殊的信号给待连接的设备。该信号包含了Wi-Fi网络的必要信息,如网络名称(SSID)和密码。当设备接收到信号后,它会自动连接到指定的Wi-Fi网络,无需用户手动输入。

(3)优点

        Wi-Fi SmartConfig的主要优点是简单和方便。它消除了用户手动输入长而复杂的密码或操作复杂设置过程的需求。用户只需启动SmartConfig过程,让设备自动连接到Wi-Fi网络。

需要注意的是,Wi-Fi SmartConfig的实现可能因设备和制造商而异。一些设备可能具有自己的专有SmartConfig方法,而其他设备可能使用标准化协议,如ESP8266 SmartConfig或TI的SmartConfig。

2.Airkiss(本质上使用的就是Smartconfig)

        Airkiss是一种基于声音传输的Wi-Fi配置技术,用于简化设备连接到Wi-Fi网络的过程。它通常用于物联网(IoT)设备,包括智能家居设备、智能电器和可穿戴设备。

        Airkiss基于声音传输的原理,通过将Wi-Fi网络的SSID和密码编码为声音信号,并通过设备的麦克风接收这些声音信号来实现设备的自动配置。用户只需在移动应用程序上输入Wi-Fi网络的信息,并将手机的扬声器靠近待连接的设备的麦克风,设备就可以通过识别和解码声音信号来自动连接到Wi-Fi网络。

        Airkiss的优点是它消除了用户手动输入Wi-Fi网络信息的需求,简化了设备的配置过程。它特别适用于没有显示屏或键盘的设备,因为它不需要用户手动输入复杂的密码。

3.代码修改

1.使用上节的httpserver工程(继续修改)

2.对user_init进行修改

    printf("SDK version:%s\n", system_get_sdk_version());
    led_init();
    wifi_set_opmode(STATION_MODE);

	//这是让其他器件入自己的网络
    xTaskCreate(smartconfig_task, "smartconfig", 256, NULL, 5, NULL);
	HttpServer_init();

3.对user_main.c的smartconfig_done函数进行修改

//airkiss_start_discover();

4.功能验证

1. 开始扫描信道

2.手机智能配置APP通过某种协议包发送家里路由器的SSID和密码

(1)手机微信扫码(这是物联网设备连接的服务小程序)

(2)手机打开位置信息,输入wifi密码(注意,需要和esp8266连接在同一个wifi下

(3)连接wifi

l

3.wifi设备通过抓包获取到SSID和密码,然后连接家里的路由器

4.在网页中输入192.168.3.126,这是esp8266的ip

四。SNTP协议

1.SNTP协议

        NTP 是网络时间协议(Network Time Protocol),它用来同步网络设备【如计算机、手机】的时间的协议。

        简单来说,就是从外部获取时钟,并与内部时钟校对。

        SNTP由NTP改编而来(简单网络时间协议,Simple Network Time Protocol)大大简化了NTP协议,同时也能保证时间达到一定的精确度。在实际应用中,SNTP协议主要被用来同步因特网上计算机的时间。

        SNTP协议采用客户端/服务器的工作方式,可以采用单播(点对点)或者广播(一点对多点)模式操作。SNTP服务器通过接收GPS信号或自带的原子钟作为系统的时间基准。单播模式下,SNTP客户端能够通过定期访问SNTP服务器获得准确的时间信息,用于调整客户端自身所在系统的时间,达到同步时间的目的。广播模式下,SNTP服务器周期性地发送消息给指定的IP广播地址或者IP多播地址。SNTP客户端通过监听这些地址来获得时间信息。

NTP 服务器列表

0.NTP在哪里用到

        计算机同步时钟,SNTP就是small NTP。

        1.最常见、熟知的就是 www.pool.ntp.org/zone/cn,国内地址为:cn.pool.ntp.org。

       2. Windows 系统上自带的俩个:time.windows.com 和 time.nist.gov。

       3. Mac OS X 上自带的俩个:time.apple.com 和 time.asia.apple.com。

       4. 一个国内无偿提供的 NTP 服务器,速度挺快,但地址池有两个 IP 已不可用,我已邮件给官方。官网:NTP授时快速域名服务,NTP 服务器:cn.ntp.org.cn。

        5.来自阿里云的 NTP 服务器:

                ntp1.aliyun.com

                ntp2.aliyun.com

                ntp3.aliyun.com

                ntp4.aliyun.com

                ntp5.aliyun.com

                ntp6.aliyun.com

                ntp7.aliyun.com

2.SNTP接口说明

void sntp_setserver(unsigned char idx, ip_addr_t *addr);

功能:通过 IP 地址设置 SNTP 服务器,一共最多支持设置 3 个 SNTP 服务器。

参数:

    (1)unsigned char idx :SNTP 服务器编号,最多⽀持3个 SNTP 服务器(0~2);0 号为主服务器,1号和2 号为备用服务器。

    (2)ip_addr_t *addr :IP 地址;用户需自行确保,传入的是合法SNTP服务器。

返回:        无

void sntp_setservername(unsigned char idx, char *server);

1.功能:通过域名设置 SNTP 服务器,一共最多支持设置 3 个 SNTP 服务器。

2.参数:

     (1)unsigned char idx :SNTP 服务器编号,最多⽀持3个SNTP服务器(0 ~ 2);0号为主服务器,1号和2号为备用服务器。

    (2)char *server :域名;用户需自行确保,传入的是合法 SNTP 服务器。

3.返回:无

sntp_init

功能:SNTP 初始化

函数定义:void sntp_init(void)

参数:无

返回:无

sntp_stop

功能:SNTP 关闭

函数定义:void sntp_stop(void)

参数:无

返回:无

sntp_get_current_timestamp

功能:查询当前距离基准时间( 1970.01.01 00: 00: 00 GMT + 8)的时间戳,单位:秒

函数定义:uint32 sntp_get_current_timestamp()

参数:无

返回:距离基准时间的时间戳

sntp_get_real_time

功能:查询实际时间( GMT + 8)

函数定义:char* sntp_get_real_time(long t)

参数:long t - 与基准时间相距的时间戳

返回:实际时间

3.SNTP功能实现

(1)功能分析

1.定时5秒串口打印一次,网络时间

2.在freeRTOS下新建SNTP任务实现

(2)代码流程

1.user中use_sntp.c

#include "esp_common.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"
#include "lwip/apps/sntp.h"

#include "user_sntp.h"
char* SntpServerNames[3] ={

"ntp1.aliyun.com",
"ntp2.aliyun.com",
"ntp3.aliyun.com"
};
/******************************************************************************
 * FunctionName : ATaskSntp
 * Description  : ATaskSntp 任务
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
void ATaskSntp( void *pvParameters )
{
    STATION_STATUS Status;
    uint32 time;

    do{
        Status = wifi_station_get_connect_status();
        vTaskDelay(100);
    
    }while(Status != STATION_GOT_IP);
    printf("task is SNTP\n");
    printf("STATION_GOT_IP!\n");
    sntp_setservername(0,SntpServerNames[0]);
    sntp_setservername(1,SntpServerNames[1]);
    sntp_setservername(2,SntpServerNames[2]);
    sntp_init();
    for(;;)
    {
        time = sntp_get_current_timestamp();
        if(time)
        {
            printf("current date:%s\n",sntp_get_real_time(time));          
        }
        vTaskDelay(500);
    }
    vTaskDelete(NULL);
}
/******************************************************************************
 * FunctionName : Sntp_init
 * Description  : Sntp_init 初始化
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
void Sntp_init(void)
{
    xTaskCreate(ATaskSntp, "Sntp", 512, NULL, 4, NULL);
}

2.include中use_sntp.h

#ifndef __USER_SNTP_H__
#define __USER_SNTP_H__

#ifdef __cplusplus
extern "C" {
#endif

void ATaskSntp( void *pvParameters );
void Sntp_init(void);

#ifdef __cplusplus
}
#endif

#endif

代码具体解释

1.判断是否获取到IP地址

2.设置NTP 服务器

3.SNTP初始化

4.延时5秒钟

5.获取时间戳

6.打印 实时时间

(3)对代码的具体修改

(1)sntp中的宏定义的修改()

(2)宏定义改成3

(4)对user_main修改

    printf("SDK version:%s\n", system_get_sdk_version());

    wifi_set_opmode(STATION_MODE);
    struct station_config *config = (struct station_config *)\
        zalloc(sizeof(struct station_config));
    sprintf(config->ssid,SSID);
    sprintf(config->password,PASSWORD);

    wifi_station_set_config(config);
    free(config);

    wifi_station_set_auto_connect(TRUE);
    
    Sntp_init();
    HttpServer_init();

        需要入网,之后使用SNTP协议校对时间。

运行结果

        五秒打印时间

五。远程升级OTA

1.FOTA远程固件升级

        FOTA(Firmware Over-The-Air)移动终端的空中下载软件升级,指通过云端升级技术,为具有连网功能的设备:例如手机、平板电脑、便携式媒体播放器、移动互联网设备等提供固件升级服务,用户使用网络以按需、易扩展的方式获取智能终端系统升级包,并通过FOTA进行云端升级,完成系统修复和优化。

ESP8266远程升级

        ESP8266能通过网络远程更新固件,需要将编译生成的user1.bin和user2.bin放到服务器上,可以使用用户自己的服务器,也可以使用乐鑫官方提供的免费服务器。

    (1)使用用户自己的服务器,这方式比较简单,将新的固件放到服务器的路径下, ESP8266使用http get的方式下载新的固件写到FLASH里面,然后重启运行新的固件。

    (2)使用乐鑫官方提供的免费服务器,这种方式要复杂一些,毕竟使用别人的东西,需要遵循别人定的规则,首先要从乐鑫官网申请一个key(密匙),将这个密匙写到程序中,再将新的固件放到乐鑫的服务器上,然后填写一些和固件相关的信息,程序中也要有相关的部分。程序可参照官方的: ESP8266 IOT PLATFORM,文档可参考: 99CESP8266_FOTA_UPGRADE_CN.PDF。

注意:我们使用的是从网上找下的服务器运行(简单)。

Flash空间划分

2.功能要求

1.打开这个MiniWebServer

2.获取到本地IP地址,选择一个文件夹(D盘web文件夹)

        在局域网内运行WebServer,将固件放到WebServer的目录下,使用ComBox建立TCP连接,触发ESP8266从WebServer上下载新固件,然后ESP8266重启,运行新固件。

3.功能实现

(1)Upgrade源码移植

1.RTOS SDK中没有upgrade源码,此源码已放到课程资料里upgrade文件夹内为FOTA实现 2.Upgradefirmware.c为固件升级操作实现

具体实现

3.在SDK目录下新建upgrade文件

4.拷贝httpserver目录下文件到upgrade目录下

5.Upgradefirmware.c放在upgrade/user目录下

6.Upgradefirmware.h放在upgrade/include目录下

7.upgrade放在upgrade/目录下

8.修改Makefile添加编译目录

SUBDIRS=    \
	user\
	driver\
	upgrade

COMPONENTS_eagle.app.v6 = \
	user/libuser.a \
	driver/libdriver.a\
	upgrade/libupgrade.a

insightSource

1.添加文件

2.同步

4.代码实现

1.user_main

user_init的修改

    printf("SDK version:%s\n", system_get_sdk_version());

    if(system_upgrade_userbin_check() == UPGRADE_FW_BIN1){
        printf("user1 is runing!\n");
    }
    else{
       printf("user2 is runing!\n");
    }
    
    wifi_set_opmode(STATION_MODE);
    struct station_config *config = (struct station_config *)\
    zalloc(sizeof(struct station_config));
    sprintf(config->ssid,SSID);
    sprintf(config->password,PASSWORD);

    wifi_station_set_config(config);
    free(config);

    wifi_station_set_auto_connect(TRUE);
    HttpServer_init();

2.Upgradefirmware.c

#include "esp_common.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"
#include "upgrade.h"

/******************************************************************************
 * FunctionName : DeviceUpgradeRsp
 * Description  : DeviceUpgradeRsp
 * Parameters   : void *arg
 * Returns      : void
*******************************************************************************/
LOCAL void  
DeviceUpgradeRsp(void *arg)
{
	struct upgrade_server_info *server = arg;

	if (server->upgrade_flag == true) 	{
		printf("upgarde_successfully\n");
	} 
	else 	{
		printf("upgrade_failed\n");
	}

	if(server != NULL)	{
		free(server->url);
		server->url = NULL;
		free(server);
		server = NULL;
	}
}
/******************************************************************************
 * FunctionName : ExcuteUpgrade
 * Description  : ExcuteUpgrade
 * Parameters   : char * FirmWareDownLoadPath
 * Returns      : bool
*******************************************************************************/
bool ExcuteUpgrade( char * FirmWareDownLoadPath )
{
	char * Url = FirmWareDownLoadPath;
	
	struct	hostent hostinfo,*phost;
	char buf[101];
	char hostname[100];
	char UserBinStr[10];
	int ret;
	char *ipaddr = NULL;
	char *ptr , *path;

	if( Url == NULL )	{
		printf("error:have no url!\r\n");
		return ;
	}

	if( strncmp( Url , "http://" , 7) == 0 )	{ 
		Url += 7;
	}
	
	ptr = strchr( Url , '/' );
	if( ptr == NULL )	{
		printf("error:url parse!\r\n");
		return ;
	}
	path = ptr;
	
	memcpy( hostname , Url , ptr - Url );
	hostname[ ptr - Url ] = 0x00;

	err_t err;
	uint8_t CycleNum = 0;

	if( inet_addr( hostname ) == INADDR_NONE )	{
		do{
			if( CycleNum > 0 )	{
				printf("get host by name Count:%d!\r\n" , CycleNum );
				vTaskDelay( 100 / portTICK_RATE_MS );
			}
			err = gethostbyname_r( hostname , &hostinfo , buf , 100 , &phost , &ret );
		}
		while( ( err != ERR_OK ) && ( ++CycleNum < 5 ) );
	
		if( err == ERR_OK )	{

			int i;

			for( i = 0; hostinfo.h_addr_list[i]; i++ )
			{
				ipaddr = inet_ntoa( *(struct in_addr*)hostinfo.h_addr_list[i] );
				if( ipaddr != NULL )
				{
	//				printf("host addr is:%s\n",  ipaddr );
					break;
				}
			}

			if( ipaddr == NULL )
			{
				printf("error:get ip fail!\r\n");
				return ;
			}
		}
		else
		{
			printf( "error:gethostbyname\r\n" );
			return ;
		}
	}
	else
	{
		ipaddr = hostname;
		printf("host name is ip!\r\n");
	}
	
	struct upgrade_server_info *UpgradeServer = NULL;

	UpgradeServer = (struct upgrade_server_info *)zalloc( sizeof( struct upgrade_server_info ) );

	bzero(&UpgradeServer->sockaddrin, sizeof(struct sockaddr_in) );
	
	UpgradeServer->sockaddrin.sin_family = AF_INET;
	UpgradeServer->sockaddrin.sin_addr.s_addr = inet_addr( ipaddr );
	UpgradeServer->sockaddrin.sin_port = htons( 80 );
	UpgradeServer->sockaddrin.sin_len = sizeof( struct sockaddr );

	UpgradeServer->check_cb = DeviceUpgradeRsp;

	if (UpgradeServer->url == NULL) 
	{
		UpgradeServer->url = (uint8 *)zalloc(256);
	}

	if ( system_upgrade_userbin_check( ) == UPGRADE_FW_BIN1 ) 
	{
		strcpy( UserBinStr , "user2.bin" );
	}
	else
	{
		strcpy( UserBinStr , "user1.bin" );
	}
	UserBinStr[9] = '\0';

	sprintf(	UpgradeServer->url , "GET %s/%s HTTP/1.0\r\nHost: %s\r\nConnection: keep-alive\r\nCache-Control: no-cache\r\nAccept: */*\r\n\r\n" , path , UserBinStr , hostname );
	printf("%s" , UpgradeServer->url );
	if( system_upgrade_start( UpgradeServer ) == true )
	{
		return true;
	}
	return false;
}

3.Upgradefirmware.h

#ifndef __UPGRADEFIRMWARE_H__
#define __UPGRADEFIRMWARE_H__

#ifdef __cplusplus
extern "C" {
#endif

bool ExcuteUpgrade( char * FirmWareDownLoadPath );

#ifdef __cplusplus
}
#endif

#endif

4.对httpserver.c进行修改(这里直接全部粘贴)

        这里处理,分析从socket客户端的字符串

#include "esp_common.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"
#include "httpserver.h"
#include "upgradefirmware.h"

#define SERVERADDR "192.168.31.158"
#define SERVERPORT 80

const char *DefaultPage=
"<html>"
"<head><title>Default</title></head>"
"<body>I am Http server!</body>"
"</html>"
;

// 发送200 ok报头
int file_ok(int cfd, long flen)
{
    char *send_buf = zalloc(sizeof(char)*100);
    sprintf(send_buf, "HTTP/1.1 200 OK\r\n");
    send(cfd, send_buf, strlen(send_buf), 0);
    sprintf(send_buf, "Connection: keep-alive\r\n");
    send(cfd, send_buf, strlen(send_buf), 0);
    sprintf(send_buf, "Content-Length: %ld\r\n", flen);
    send(cfd, send_buf, strlen(send_buf), 0);
    sprintf(send_buf, "Content-Type: text/html\r\n");
    send(cfd, send_buf, strlen(send_buf), 0);
    sprintf(send_buf, "\r\n");
    send(cfd, send_buf, strlen(send_buf), 0);
    free(send_buf);
    return 0;
}
/******************************************************************************
 * FunctionName : ATaskHttpServer
 * Description  : ATaskHttpServer 任务
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
void ATaskHttpServer( void *pvParameters )
{
    int iVariableExample = 0;
    int fd = -1;
    int cfd = -1;
    int NetTimeOnt = 2000;
    int ret;

    struct sockaddr_in ServerAddr;
    struct sockaddr ClientAddr;
    socklen_t ClientAddrlen = sizeof(struct sockaddr);
    char *Httpmsg;
    char *Sendmsg;

    STATION_STATUS StaStatus;
    do
    {
        StaStatus = wifi_station_get_connect_status();
        vTaskDelay(100);
    }while(StaStatus != STATION_GOT_IP);
  
    fd = socket(PF_INET,SOCK_STREAM,0);
    if(fd == -1)
    {
        printf("get socket fail!\n");
        vTaskDelete(NULL);
        return;
    }

    setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,&NetTimeOnt,sizeof(int));

    memset(&ServerAddr,0,sizeof(ServerAddr));
    ServerAddr.sin_family = AF_INET;
    ServerAddr.sin_addr.s_addr = INADDR_ANY;
    ServerAddr.sin_port = htons(SERVERPORT);
    ServerAddr.sin_len = sizeof(ServerAddr);

    if(bind(fd,(struct sockaddr*)&ServerAddr,ServerAddr.sin_len) != 0)
    {     
        printf("bind socket fail!\n");
        vTaskDelete(NULL);
        return;
    }
    if(listen(fd,5) != 0)
    {
        printf("listen socket fail!\n");
        vTaskDelete(NULL);
        return;
    }
    Httpmsg = (char*)zalloc(sizeof(char)*1000);
    for(;;)
    {       
        cfd = accept(fd,&ClientAddr,&ClientAddrlen);
        if(cfd != -1)
        {          
            printf("HttpClient accept\n");
            ret = recv(cfd,Httpmsg,1000,0);
            if(ret > 0)
            {
                if( strncmp( Httpmsg , "UpgradeFirmware:" , 16 ) == 0 ) 
                {
                if( ExcuteUpgrade( (char *)&Httpmsg[16] ) == true )
                 {
                  send( cfd , "Upgrade is Excuted!\r\n" , strlen( "Upgrade is Excuted!\r\n" ) , 0 );
                   }
                     else
                   {
                     send( cfd , "Upgrade fail!\r\n" , strlen( "Upgrade fail!\r\n" ) , 0 );
                 }
                }
            }
            else
            {
                printf("HttpClient data is no!\n");
            }
        }
        close(cfd);
    }
    free(Httpmsg);
    vTaskDelete( NULL );
}
/******************************************************************************
 * FunctionName : HttpServer_init
 * Description  : HttpServer_init 初始化
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
void HttpServer_init(void)
{
    xTaskCreate(ATaskHttpServer, "HttpServer", 256, NULL, 4, NULL);
}

(2)升级代码的接口

固件更新函数:system_upgrade_start(struct upgrade_server_info*server);

下面是对代码的解释

(3)升级功能开发

1.struct sockaddr_in sockaddrin存放固件的服务器地址;

2.upgrade_states_check_callback check_cb固件下载完成后的回调函数

3.uint8 *url; http请求报文,包含起始行和首部。

4.需要编写两个函数,执行升级的函数和估计下载成功后的回掉函数

    (1)bool Excuteupgrade( char * Firmwaredownloadpath )执行升级的函数,参数为固件存放路径

    (2)ocal void DeviceUpgradeRsp(void *arg),固件下载完成的回掉函数,检测pgrade_flag标志,判断升级是否成功。

(4)执行升级业务流程

1.解析URL获取IP或域名

2.初始化Upgrade_server_info

3.赋值 addr信息

4.赋值 回调函数

5.赋值 URL

6.开启远程 升级任务

(5)升级完成回调业务流程

1.判断升级是否成功

2.打印升级 状态

3.释放Upgrade_server_info

(6)HttpServer业务流程

1.解析客户端字符串

2.判读是否是触发升级

3.调用升级 接口

编译:

1.使用两次编译:第一次编译使用user1.bin,第二次编译使用user2.bin

这是第二次编译(当选择y表示继续运行后,输入2选择user1.bin,不像第一次一样选择1使用user.2)

结果:

1.连接正确

2.进入课程中web服务器

(1)webserver服务器

这就是使用远程升级固件所需要的软件服务器。

(2)建立socket客户端,目标IP192.168.3.126(这是wifi的IP) ,下面是电脑无线ip  

UpgradeFirmware:http://192.168.3.12/

(3)运行结果

运行不成功

运行但是没有变成bin2

下面

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值