打印
[其他ST产品]

STM32 W5500 Http Client Get请求 下载bin文件思路和实现

[复制链接]
391|17
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
这两天在做STM32 W5500通过HTTP GET请求的方式下载bin文件,以实现OTA在线升级,到网上查了一圈,发现并没有很多有效的信息和资料。于是我就实现了一下,把思路和实现过程分享出来。

实现W5500文件下载的几个前提:

1、STM32 W5500的基础配置,使得 电脑端的CMD命令窗口能够PING通W5500,《STM32F103RC驱动W5500入网,并可ping通》。

2、STM32 W5500的TCP Client收发数据测试没有问题,《STM32F1 W5500 TCP Client 回环测试》。

3、一个可用的文件服务器,文件的URL地址放到浏览器的地址栏后,可以正常下载文件。

4、对HTTP协议有基本的认识。

使用特权

评论回复
评论
初级工程渣 2023-1-15 17:39 回复TA
———————————————— 版权声明:本文为CSDN博主「Mr_Johhny」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/lnniyunlong99/article/details/105559284/ 
沙发
初级工程渣|  楼主 | 2023-1-15 17:40 | 只看该作者
文件的URL地址放到浏览器的地址栏后,通过Wireshark工具,先看看文件下载的过程,都发生可一些什么

使用特权

评论回复
板凳
初级工程渣|  楼主 | 2023-1-15 17:40 | 只看该作者

使用特权

评论回复
地板
初级工程渣|  楼主 | 2023-1-15 17:42 | 只看该作者
下载请求的header信息,包括以下几个关键信息:

1、请求方式+接口名称+协议和协议版本(GET /file/FLASH_OPER.bin_1.1.3 HTTP/1.1\r\n)

2、文件服务器主机IP+端口 (举例:192.168.1.105:8888\r\n)

3、连接状态(Connection: Keep-Alive\r\n)

4、请求端名称(User-Agent: W5500\r\n)

5、请求端接收的编码(Accept-Encoding: gzip,deflate\r\n)

6、请求头结束标记(\r\n)

使用特权

评论回复
5
初级工程渣|  楼主 | 2023-1-15 17:43 | 只看该作者
以上是请求的分析,文件服务器返回的信息如下:

使用特权

评论回复
6
初级工程渣|  楼主 | 2023-1-15 17:44 | 只看该作者

使用特权

评论回复
7
初级工程渣|  楼主 | 2023-1-15 17:44 | 只看该作者
从文件服务器返回来的数据分析,报文和文件内容不是一次性传输的,是流的方式多次传输的。

使用特权

评论回复
8
初级工程渣|  楼主 | 2023-1-15 18:55 | 只看该作者
返回的报文,包含以下几个关键点:

1、返回的状态码(HTTP/1.1 200\r\n)

2、接收范围 字节流(Accept-Ranges: bytes\r\n)

3、内容长度(Content-Length: 3904\r\n)

4、头结束标记(\r\n)

5、字节流Data长度和Content-Length是相等的。

以上是基于浏览器下载文件的过程分析的,那么STM32 W5500下载文件的过程,模拟浏览器下载文件,从理论上来讲也是会实现的。

HTTP协议是在TCP的基础上封装的协议,W5500的TCP Client端与服务端简历连接的过程在此不描述。

使用特权

评论回复
9
初级工程渣|  楼主 | 2023-1-15 19:04 | 只看该作者
STM32 W5500下载文件有几个难点:

1、解析文件服务器返回的报文,判断返回码是否为200,只有200才代表成功;判断是否为字节流;解析出文件的大小(长度)

使用特权

评论回复
10
初级工程渣|  楼主 | 2023-1-15 19:08 | 只看该作者
2、文件字节流和返回报文的分割,以及缓存数组的数据反复搬运(毕竟W5500 一次最多可以收2Kbytes 的数据)。

使用特权

评论回复
11
初级工程渣|  楼主 | 2023-1-15 19:09 | 只看该作者
3、判断文件下载是否完成的依据。

使用特权

评论回复
12
初级工程渣|  楼主 | 2023-1-15 19:09 | 只看该作者
STM32 W5500请求和接收的过程:


使用特权

评论回复
13
初级工程渣|  楼主 | 2023-1-15 19:11 | 只看该作者
即发送一次请求报文后,不断接收文件服务器返回的信息,结果是要么接收到文件字节流和 Content-Length的相等,文件下载成功,要么各种原因失败(连接超时,读取超时,返回码不正确,不是字节流等)。

使用特权

评论回复
14
初级工程渣|  楼主 | 2023-1-15 19:12 | 只看该作者
好像该讲的都讲了,下面给出STM32 W5500 Http GET方式下载文件的代码(我测试下载 4k/13k/67k 左右的文件都可以),例程中打印了 各个分支的log信息,将文件下载的内容通过串口DMA的方式打印出来。(工程可以看我的 基础配置文章下载,再将这个主测试函数替换)

使用特权

评论回复
15
初级工程渣|  楼主 | 2023-1-15 19:13 | 只看该作者
#ifndef __STM32F10X_H
#define __STM32F10X_H
#include "stm32f10x.h"
#endif

#ifndef __Z_UTIL_TIME_H
#define __Z_UTIL_TIME_H
#include "z_util_time.h"
#endif

#ifndef __Z_HARDWARE_LED_H
#define __Z_HARDWARE_LED_H
#include "z_hardware_led.h"
#endif

#ifndef __Z_HARDWARE_SPI_H
#define __Z_HARDWARE_SPI_H
#include "z_hardware_spi.h"
#endif

#ifndef __W5500_H
#define __W5500_H
#include "w5500.h"
#endif

#ifndef __SOCKET_H
#define __SOCKET_H
#include "socket.h"
#endif

#ifndef __W5500_CONF_H
#define __W5500_CONF_H
#include "w5500_conf.h"
#endif

#ifndef __DHCP_H
#define __DHCP_H
#include "dhcp.h"
#endif

#ifndef __Z_HARDWARE_USART2_H
#define __Z_HARDWARE_USART2_H
#include "z_hardware_usart2.h"
#endif

#include <stdlib.h>

void log_net(char *log);       

void func_pack_http_get_download_header(u8* buff, u16 size_buff, u8* srv_ip, u16 srv_port, char* interface);
u8 func_http_get_download_file(u8 sockno, u8* srv_ip, u16 srv_port, char* interface, u16 timeout_ms, u8* buff, u16 size_buff);
u8 func_analysis_http_download_header(u8* buffer, u16 len_buf, u8 *resp_code, u8 *is_stream, u32 *cont_len);

u8 buf[2048];
int main(void)
{
       
        u8 mac[6]={0, };
        DHCP_Get dhcp_get;
       
        //FIXME input your server ip to download file
        u8 srv_ip[] = {192, 168, 1, 105};
        u16 srv_port = 8888;
       
       
        systick_configuration();
        init_led();
        init_hardware_usart2_dma(115200);
       
        init_system_spi();
        func_w5500_reset();       
       
        getMacByLockCode(mac);
        setSHAR(mac);
       
        sysinit(txsize, rxsize);
        setRTR(2000);
  setRCR(3);
       
        //USART DMA problem: 2 bytes missing
        func_usart2_dma_send_bytes(mac, 2);
        delay_ms(100);
       
        //DHCP
        for(;func_dhcp_get_ip_sub_gw(1, mac, &dhcp_get, 500) != 0;);       
        if(func_dhcp_get_ip_sub_gw(1, mac, &dhcp_get, 500) == 0)
        {
                setSUBR(dhcp_get.sub);
                setGAR(dhcp_get.gw);
                setSIPR(dhcp_get.lip);
                close(1);
               
                log_net("0");
        }
       
        memset(buf, 0 , sizeof(buf));
        func_http_get_download_file(0, srv_ip, srv_port, "/file/FLASH_OPER.bin_1.1.3", 5000, buf, sizeof(buf));
       
        for(;;)
        {
                func_led1_toggle();
                delay_ms(500);       
        }
}

u8 func_http_get_download_file(u8 sockno, u8* srv_ip, u16 srv_port, char* interface, u16 timeout_ms, u8* buff, u16 size_buff)
{
        u16 local_port = 60000, len = 0, cnt = 0;
        u8 sendok = 0, recv_start = 0;
       
        u8 cache[2048];
        u16 cache_cont_len;
        u32 cont_len, download_cont_len;
       
        func_pack_http_get_download_header(buff, size_buff, srv_ip, srv_port, interface);
       
//        setkeepalive(sockno);//auto
        IINCHIP_WRITE(Sn_KPALVTR(sockno),0x00);//manual
       
        memset(cache, 0 , sizeof(cache));
        cache_cont_len = 0;
       
        for(;;)
        {
                switch(getSn_SR(sockno))
                {
                        case SOCK_INIT:
                                        connect(sockno, srv_ip, srv_port);
                        break;
                        case SOCK_ESTABLISHED:
                        {
                                if(sendok == 0)
                                {
                                        log_net("1");
                                       
                                        len = strlen((char *)buff);
                                        send(sockno, buff, len);
                                        sendok ++;
                                        if(getSn_IR(sockno) & Sn_IR_CON)                                          
                                        {
                                                setSn_IR(sockno, Sn_IR_CON);
                                                break;
                                        }
                                }
                               
                                len = getSn_RX_RSR(sockno);
                                if(len > 0)
                                {
                                        cnt = 0;
                                       
                                        memset(buff, 0, size_buff);
                                        len = recv(sockno, buff, len);
                                        if(recv_start == 0)
                                        {
                                                u8 res, resp_code, isstream;
                                               
                                                log_net("2");
                                               
                                                if(cache_cont_len + len <= sizeof(cache))
                                                {
                                                        log_net("3");
                                                        memcpy(cache+cache_cont_len, buff, len);
                                                        cache_cont_len += len;
                                                       
                                                        memset(buff, 0, size_buff);
                                                        len = 0;
                                                }
                                                else
                                                {
                                                        u32 len_copy;
                                                        log_net("4");
                                                       
                                                        len_copy = sizeof(cache) - cache_cont_len;
                                                        memcpy(cache+cache_cont_len, buff, len_copy);
                                                        memcpy(buff, buff + len_copy, len - len_copy);
                                                        memset(buff + (len - len_copy), 0, size_buff - (len - len_copy));
                                                        len = len - len_copy;
                                                        cache_cont_len = sizeof(cache);
                                                       
                                                }                                                       
                                               
                                                res = func_analysis_http_download_header(cache, cache_cont_len, &resp_code, &isstream, &cont_len);
                                                if(res == 0)
                                                {
                                                        if(resp_code != 200 || isstream != 1)//response code 200, must be iostream
                                                        {
                                                                log_net("22");
                                                                return 3;// response not ok
                                                        }
                                                        recv_start++;
                                                       
                                                        //logic -> file size should be no more than 108 Kbytes
                                                        if(cont_len > 108*1024)
                                                        {
                                                                log_net("23");
                                                                return 4;
                                                        }
                                                       
                                                        //if download file done
                                                        {
                                                                u32 tmp_len;
                                                                u8* pos_header;
                                                                pos_header = (u8*)strstr((char*)cache, "\r\n\r\n");
                                                                //remove header, save file byte to cache
                                                                tmp_len = cache_cont_len - (pos_header + 4 - cache);
                                                                memcpy(cache, pos_header + 4, tmp_len);
                                                                memset(cache + tmp_len, 0 , sizeof(cache) - tmp_len);
                                                                cache_cont_len = tmp_len;
                                                                download_cont_len = cache_cont_len;
                                                               
                                                                if(len > 0)
                                                                {
                                                                        if(cache_cont_len + len <= sizeof(cache))
                                                                        {
                                                                                log_net("5");
                                                                                memcpy(cache+cache_cont_len, buff, len);
                                                                                cache_cont_len += len;                                                                               
                                                                                download_cont_len += len;
                                                                               
                                                                                memset(buff, 0, size_buff);
                                                                                len = 0;
                                                                        }
                                                                        else
                                                                        {
                                                                                u32 len_copy;
                                                                                log_net("6");
                                                                               
                                                                                len_copy = sizeof(cache) - cache_cont_len;
                                                                                memcpy(cache+cache_cont_len, buff, len_copy);
                                                                                memcpy(buff, buff + len_copy, len - len_copy);
                                                                                memset(buff + (len - len_copy), 0, size_buff - (len - len_copy));
                                                                                len = len - len_copy;
                                                                                cache_cont_len = sizeof(cache);
                                                                               
                                                                                //TODO write to FLASH backup
                                                                                func_usart2_dma_send_bytes(cache, cache_cont_len);
                                                                                memset(cache, 0, cache_cont_len);
                                                                                cache_cont_len = 0;
                                                                                memcpy(cache+cache_cont_len, buff, len);
                                                                                cache_cont_len = len;
                                                                                download_cont_len = sizeof(cache) + cache_cont_len;
                                                                               
                                                                        }
                                                                }
                                                               
                                                                if(download_cont_len == cont_len)// small file download done
                                                                {
                                                                        log_net("7");
                                                                        func_usart2_dma_send_bytes(cache, cache_cont_len);
                                                                        //TODO write to FLASH backup and record
                                                                       
                                                                        disconnect(sockno);
                                                                        return 0;//download file done
                                                                }
                                                               
                                                        }                                                               
                                                }
                                        }
                                        else
                                        {
                                                IINCHIP_WRITE(Sn_CR(sockno),Sn_CR_SEND_KEEP);//keep-alive manual
                                               
                                                //file size is big, write to FALSH several times
                                                download_cont_len += len;
                                                if(cache_cont_len + len <= sizeof(cache))
                                                {
                                                        log_net("8");
                                                        memcpy(cache+cache_cont_len, buff, len);
                                                        cache_cont_len += len;
                                                        memset(buff, 0, size_buff);
                                                        len = 0;
                                                }
                                                else
                                                {
                                                        u32 len_copy;
                                                        log_net("9");
                                                        len_copy = sizeof(cache) - cache_cont_len;
                                                        memcpy(cache+cache_cont_len, buff, len_copy);
                                                        memcpy(buff, buff + len_copy, len - len_copy);
                                                        memset(buff + (len - len_copy), 0, size_buff - (len - len_copy));
                                                        len = len - len_copy;
                                                        cache_cont_len = sizeof(cache);
                                                       
                                                        //TODO write to FLASH backup
                                                        func_usart2_dma_send_bytes(cache, cache_cont_len);
                                                        memset(cache, 0, cache_cont_len);
                                                        cache_cont_len = 0;
                                                        memcpy(cache+cache_cont_len, buff, len);
                                                        cache_cont_len = len;
                                                }                                               
                                               
                                                //if donwload file done
                                                if(download_cont_len == cont_len)
                                                {
                                                        log_net("10");
                                                        //TODO write to FLASH backup and record
                                                        func_usart2_dma_send_bytes(cache, cache_cont_len);
                                                        disconnect(sockno);
                                                        return 0;//download file done
                                                }                                                       
                                        }
                                }
                        }                       
                        break;
                        case SOCK_CLOSE_WAIT:
                                        close(sockno);
                                        log_net("21");
                                        return 2;//read timeout
                        case SOCK_CLOSED:
                                        close(sockno);
                                        sendok = 0;
                                        recv_start = 0;
                                        if(local_port > 65400)
                                        {
                                                local_port = 60000;
                                        }
                                        socket(sockno, Sn_MR_TCP, local_port++, Sn_MR_ND);
                        break;
                }
               
                cnt ++;
                if(cnt >= timeout_ms)
                {
                        log_net("20");
                        return 1;//connect timeout
                }
                delay_ms(1);
        }
}

u8 func_analysis_http_download_header(u8* buffer, u16 len_buf, u8 *resp_code, u8 *is_stream, u32 *cont_len)
{
        *resp_code = 0;
        *is_stream = 0;
        *cont_len = 0;
        if(strstr((char*)buffer, "\r\n\r\n") != NULL)//response header ok
        {
                char *p1, *p2;
               
                p1 = strstr((char*)buffer, "HTTP/1.1 200");       
                if(p1 != NULL)// io stream
                {
                        *resp_code = 200;
                }               
               
                p1 = strstr((char*)buffer, "Accept-Ranges: bytes");       
                if(p1 != NULL)// io stream
                {
                        *is_stream = 1;
                }
               
                p1 = strstr((char*)buffer, "Content-Length: ");                                                                               
                if(p1 != NULL)
                {
                        p2 = strstr(p1, "\r\n");
                        if(p2 != NULL)
                        {
                                char str_len[8] = {0,};
                                memcpy(str_len, p1 + strlen("Content-Length: "), p2 - p1 - strlen("Content-Length: "));
                                *cont_len = atoi(str_len);
                                return 0;
                        }
                }
        }
        return 1;
}

void log_net(char *log)
{
        u8 logsrv_ip[] = {192, 168, 1, 105};
        socket(2, Sn_MR_UDP, 7890, 0x00);
        sendto(2, (u8*)log, strlen(log), logsrv_ip, 9876);
}

void func_pack_http_get_download_header(u8* buff, u16 size_buff, u8* srv_ip, u16 srv_port, char* interface)
{
        u8 tmp[64];
        memset(buff, 0, size_buff);
        //header
        memcpy(buff, "GET ", strlen("GET "));
        strcat((char *)buff, interface);
        strcat((char *)buff, " HTTP/1.1\r\n");
        memset(tmp, 0 , sizeof(tmp));
        sprintf((char*)tmp, "Host: %d.%d.%d.%d:%d\r\n", srv_ip[0], srv_ip[1], srv_ip[2], srv_ip[3], srv_port);
        strcat((char *)buff, (char*)tmp);
        strcat((char *)buff, "Connection: Keep-Alive\r\n");
        strcat((char *)buff, "User-Agent: W5500\r\n");
        strcat((char *)buff, "Accept-Encoding: gzip,deflate\r\n");
        strcat((char *)buff, "\r\n");
}

使用特权

评论回复
16
初级工程渣|  楼主 | 2023-1-15 19:16 | 只看该作者
最后,附上我测试的效果图:

使用特权

评论回复
17
初级工程渣|  楼主 | 2023-1-15 19:17 | 只看该作者
串口打印的字节数和 Content-Length的长度一致,并和原bin文件做了对比,内容一致。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

47

主题

516

帖子

0

粉丝