[其他ST产品] STM32 W5500 Http Client Get请求 下载bin文件思路和实现

[复制链接]
 楼主| 初级工程渣 发表于 2023-1-15 17:38 | 显示全部楼层 |阅读模式
这两天在做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协议有基本的认识。

评论

———————————————— 版权声明:本文为CSDN博主「Mr_Johhny」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/lnniyunlong99/article/details/105559284/  发表于 2023-1-15 17:39
 楼主| 初级工程渣 发表于 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)
 楼主| 初级工程渣 发表于 2023-1-15 17:43 | 显示全部楼层
以上是请求的分析,文件服务器返回的信息如下:
 楼主| 初级工程渣 发表于 2023-1-15 17:44 | 显示全部楼层
 楼主| 初级工程渣 发表于 2023-1-15 17:44 | 显示全部楼层
从文件服务器返回来的数据分析,报文和文件内容不是一次性传输的,是流的方式多次传输的。
 楼主| 初级工程渣 发表于 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端与服务端简历连接的过程在此不描述。
 楼主| 初级工程渣 发表于 2023-1-15 19:04 | 显示全部楼层
STM32 W5500下载文件有几个难点:

1、解析文件服务器返回的报文,判断返回码是否为200,只有200才代表成功;判断是否为字节流;解析出文件的大小(长度)
 楼主| 初级工程渣 发表于 2023-1-15 19:08 | 显示全部楼层
2、文件字节流和返回报文的分割,以及缓存数组的数据反复搬运(毕竟W5500 一次最多可以收2Kbytes 的数据)。
 楼主| 初级工程渣 发表于 2023-1-15 19:09 | 显示全部楼层
3、判断文件下载是否完成的依据。
 楼主| 初级工程渣 发表于 2023-1-15 19:09 | 显示全部楼层
STM32 W5500请求和接收的过程:

9715263c3defab96d0.png
 楼主| 初级工程渣 发表于 2023-1-15 19:11 | 显示全部楼层
即发送一次请求报文后,不断接收文件服务器返回的信息,结果是要么接收到文件字节流和 Content-Length的相等,文件下载成功,要么各种原因失败(连接超时,读取超时,返回码不正确,不是字节流等)。
 楼主| 初级工程渣 发表于 2023-1-15 19:12 | 显示全部楼层
好像该讲的都讲了,下面给出STM32 W5500 Http GET方式下载文件的代码(我测试下载 4k/13k/67k 左右的文件都可以),例程中打印了 各个分支的log信息,将文件下载的内容通过串口DMA的方式打印出来。(工程可以看我的 基础配置文章下载,再将这个主测试函数替换)
 楼主| 初级工程渣 发表于 2023-1-15 19:13 | 显示全部楼层
  1. #ifndef __STM32F10X_H
  2. #define __STM32F10X_H
  3. #include "stm32f10x.h"
  4. #endif

  5. #ifndef __Z_UTIL_TIME_H
  6. #define __Z_UTIL_TIME_H
  7. #include "z_util_time.h"
  8. #endif

  9. #ifndef __Z_HARDWARE_LED_H
  10. #define __Z_HARDWARE_LED_H
  11. #include "z_hardware_led.h"
  12. #endif

  13. #ifndef __Z_HARDWARE_SPI_H
  14. #define __Z_HARDWARE_SPI_H
  15. #include "z_hardware_spi.h"
  16. #endif

  17. #ifndef __W5500_H
  18. #define __W5500_H
  19. #include "w5500.h"
  20. #endif

  21. #ifndef __SOCKET_H
  22. #define __SOCKET_H
  23. #include "socket.h"
  24. #endif

  25. #ifndef __W5500_CONF_H
  26. #define __W5500_CONF_H
  27. #include "w5500_conf.h"
  28. #endif

  29. #ifndef __DHCP_H
  30. #define __DHCP_H
  31. #include "dhcp.h"
  32. #endif

  33. #ifndef __Z_HARDWARE_USART2_H
  34. #define __Z_HARDWARE_USART2_H
  35. #include "z_hardware_usart2.h"
  36. #endif

  37. #include <stdlib.h>

  38. void log_net(char *log);       

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

  42. u8 buf[2048];
  43. int main(void)
  44. {
  45.        
  46.         u8 mac[6]={0, };
  47.         DHCP_Get dhcp_get;
  48.        
  49.         //FIXME input your server ip to download file
  50.         u8 srv_ip[] = {192, 168, 1, 105};
  51.         u16 srv_port = 8888;
  52.        
  53.        
  54.         systick_configuration();
  55.         init_led();
  56.         init_hardware_usart2_dma(115200);
  57.        
  58.         init_system_spi();
  59.         func_w5500_reset();       
  60.        
  61.         getMacByLockCode(mac);
  62.         setSHAR(mac);
  63.        
  64.         sysinit(txsize, rxsize);
  65.         setRTR(2000);
  66.   setRCR(3);
  67.        
  68.         //USART DMA problem: 2 bytes missing
  69.         func_usart2_dma_send_bytes(mac, 2);
  70.         delay_ms(100);
  71.        
  72.         //DHCP
  73.         for(;func_dhcp_get_ip_sub_gw(1, mac, &dhcp_get, 500) != 0;);       
  74.         if(func_dhcp_get_ip_sub_gw(1, mac, &dhcp_get, 500) == 0)
  75.         {
  76.                 setSUBR(dhcp_get.sub);
  77.                 setGAR(dhcp_get.gw);
  78.                 setSIPR(dhcp_get.lip);
  79.                 close(1);
  80.                
  81.                 log_net("0");
  82.         }
  83.        
  84.         memset(buf, 0 , sizeof(buf));
  85.         func_http_get_download_file(0, srv_ip, srv_port, "/file/FLASH_OPER.bin_1.1.3", 5000, buf, sizeof(buf));
  86.        
  87.         for(;;)
  88.         {
  89.                 func_led1_toggle();
  90.                 delay_ms(500);       
  91.         }
  92. }

  93. u8 func_http_get_download_file(u8 sockno, u8* srv_ip, u16 srv_port, char* interface, u16 timeout_ms, u8* buff, u16 size_buff)
  94. {
  95.         u16 local_port = 60000, len = 0, cnt = 0;
  96.         u8 sendok = 0, recv_start = 0;
  97.        
  98.         u8 cache[2048];
  99.         u16 cache_cont_len;
  100.         u32 cont_len, download_cont_len;
  101.        
  102.         func_pack_http_get_download_header(buff, size_buff, srv_ip, srv_port, interface);
  103.        
  104. //        setkeepalive(sockno);//auto
  105.         IINCHIP_WRITE(Sn_KPALVTR(sockno),0x00);//manual
  106.        
  107.         memset(cache, 0 , sizeof(cache));
  108.         cache_cont_len = 0;
  109.        
  110.         for(;;)
  111.         {
  112.                 switch(getSn_SR(sockno))
  113.                 {
  114.                         case SOCK_INIT:
  115.                                         connect(sockno, srv_ip, srv_port);
  116.                         break;
  117.                         case SOCK_ESTABLISHED:
  118.                         {
  119.                                 if(sendok == 0)
  120.                                 {
  121.                                         log_net("1");
  122.                                        
  123.                                         len = strlen((char *)buff);
  124.                                         send(sockno, buff, len);
  125.                                         sendok ++;
  126.                                         if(getSn_IR(sockno) & Sn_IR_CON)                                          
  127.                                         {
  128.                                                 setSn_IR(sockno, Sn_IR_CON);
  129.                                                 break;
  130.                                         }
  131.                                 }
  132.                                
  133.                                 len = getSn_RX_RSR(sockno);
  134.                                 if(len > 0)
  135.                                 {
  136.                                         cnt = 0;
  137.                                        
  138.                                         memset(buff, 0, size_buff);
  139.                                         len = recv(sockno, buff, len);
  140.                                         if(recv_start == 0)
  141.                                         {
  142.                                                 u8 res, resp_code, isstream;
  143.                                                
  144.                                                 log_net("2");
  145.                                                
  146.                                                 if(cache_cont_len + len <= sizeof(cache))
  147.                                                 {
  148.                                                         log_net("3");
  149.                                                         memcpy(cache+cache_cont_len, buff, len);
  150.                                                         cache_cont_len += len;
  151.                                                        
  152.                                                         memset(buff, 0, size_buff);
  153.                                                         len = 0;
  154.                                                 }
  155.                                                 else
  156.                                                 {
  157.                                                         u32 len_copy;
  158.                                                         log_net("4");
  159.                                                        
  160.                                                         len_copy = sizeof(cache) - cache_cont_len;
  161.                                                         memcpy(cache+cache_cont_len, buff, len_copy);
  162.                                                         memcpy(buff, buff + len_copy, len - len_copy);
  163.                                                         memset(buff + (len - len_copy), 0, size_buff - (len - len_copy));
  164.                                                         len = len - len_copy;
  165.                                                         cache_cont_len = sizeof(cache);
  166.                                                        
  167.                                                 }                                                       
  168.                                                
  169.                                                 res = func_analysis_http_download_header(cache, cache_cont_len, &resp_code, &isstream, &cont_len);
  170.                                                 if(res == 0)
  171.                                                 {
  172.                                                         if(resp_code != 200 || isstream != 1)//response code 200, must be iostream
  173.                                                         {
  174.                                                                 log_net("22");
  175.                                                                 return 3;// response not ok
  176.                                                         }
  177.                                                         recv_start++;
  178.                                                        
  179.                                                         //logic -> file size should be no more than 108 Kbytes
  180.                                                         if(cont_len > 108*1024)
  181.                                                         {
  182.                                                                 log_net("23");
  183.                                                                 return 4;
  184.                                                         }
  185.                                                        
  186.                                                         //if download file done
  187.                                                         {
  188.                                                                 u32 tmp_len;
  189.                                                                 u8* pos_header;
  190.                                                                 pos_header = (u8*)strstr((char*)cache, "\r\n\r\n");
  191.                                                                 //remove header, save file byte to cache
  192.                                                                 tmp_len = cache_cont_len - (pos_header + 4 - cache);
  193.                                                                 memcpy(cache, pos_header + 4, tmp_len);
  194.                                                                 memset(cache + tmp_len, 0 , sizeof(cache) - tmp_len);
  195.                                                                 cache_cont_len = tmp_len;
  196.                                                                 download_cont_len = cache_cont_len;
  197.                                                                
  198.                                                                 if(len > 0)
  199.                                                                 {
  200.                                                                         if(cache_cont_len + len <= sizeof(cache))
  201.                                                                         {
  202.                                                                                 log_net("5");
  203.                                                                                 memcpy(cache+cache_cont_len, buff, len);
  204.                                                                                 cache_cont_len += len;                                                                               
  205.                                                                                 download_cont_len += len;
  206.                                                                                
  207.                                                                                 memset(buff, 0, size_buff);
  208.                                                                                 len = 0;
  209.                                                                         }
  210.                                                                         else
  211.                                                                         {
  212.                                                                                 u32 len_copy;
  213.                                                                                 log_net("6");
  214.                                                                                
  215.                                                                                 len_copy = sizeof(cache) - cache_cont_len;
  216.                                                                                 memcpy(cache+cache_cont_len, buff, len_copy);
  217.                                                                                 memcpy(buff, buff + len_copy, len - len_copy);
  218.                                                                                 memset(buff + (len - len_copy), 0, size_buff - (len - len_copy));
  219.                                                                                 len = len - len_copy;
  220.                                                                                 cache_cont_len = sizeof(cache);
  221.                                                                                
  222.                                                                                 //TODO write to FLASH backup
  223.                                                                                 func_usart2_dma_send_bytes(cache, cache_cont_len);
  224.                                                                                 memset(cache, 0, cache_cont_len);
  225.                                                                                 cache_cont_len = 0;
  226.                                                                                 memcpy(cache+cache_cont_len, buff, len);
  227.                                                                                 cache_cont_len = len;
  228.                                                                                 download_cont_len = sizeof(cache) + cache_cont_len;
  229.                                                                                
  230.                                                                         }
  231.                                                                 }
  232.                                                                
  233.                                                                 if(download_cont_len == cont_len)// small file download done
  234.                                                                 {
  235.                                                                         log_net("7");
  236.                                                                         func_usart2_dma_send_bytes(cache, cache_cont_len);
  237.                                                                         //TODO write to FLASH backup and record
  238.                                                                        
  239.                                                                         disconnect(sockno);
  240.                                                                         return 0;//download file done
  241.                                                                 }
  242.                                                                
  243.                                                         }                                                               
  244.                                                 }
  245.                                         }
  246.                                         else
  247.                                         {
  248.                                                 IINCHIP_WRITE(Sn_CR(sockno),Sn_CR_SEND_KEEP);//keep-alive manual
  249.                                                
  250.                                                 //file size is big, write to FALSH several times
  251.                                                 download_cont_len += len;
  252.                                                 if(cache_cont_len + len <= sizeof(cache))
  253.                                                 {
  254.                                                         log_net("8");
  255.                                                         memcpy(cache+cache_cont_len, buff, len);
  256.                                                         cache_cont_len += len;
  257.                                                         memset(buff, 0, size_buff);
  258.                                                         len = 0;
  259.                                                 }
  260.                                                 else
  261.                                                 {
  262.                                                         u32 len_copy;
  263.                                                         log_net("9");
  264.                                                         len_copy = sizeof(cache) - cache_cont_len;
  265.                                                         memcpy(cache+cache_cont_len, buff, len_copy);
  266.                                                         memcpy(buff, buff + len_copy, len - len_copy);
  267.                                                         memset(buff + (len - len_copy), 0, size_buff - (len - len_copy));
  268.                                                         len = len - len_copy;
  269.                                                         cache_cont_len = sizeof(cache);
  270.                                                        
  271.                                                         //TODO write to FLASH backup
  272.                                                         func_usart2_dma_send_bytes(cache, cache_cont_len);
  273.                                                         memset(cache, 0, cache_cont_len);
  274.                                                         cache_cont_len = 0;
  275.                                                         memcpy(cache+cache_cont_len, buff, len);
  276.                                                         cache_cont_len = len;
  277.                                                 }                                               
  278.                                                
  279.                                                 //if donwload file done
  280.                                                 if(download_cont_len == cont_len)
  281.                                                 {
  282.                                                         log_net("10");
  283.                                                         //TODO write to FLASH backup and record
  284.                                                         func_usart2_dma_send_bytes(cache, cache_cont_len);
  285.                                                         disconnect(sockno);
  286.                                                         return 0;//download file done
  287.                                                 }                                                       
  288.                                         }
  289.                                 }
  290.                         }                       
  291.                         break;
  292.                         case SOCK_CLOSE_WAIT:
  293.                                         close(sockno);
  294.                                         log_net("21");
  295.                                         return 2;//read timeout
  296.                         case SOCK_CLOSED:
  297.                                         close(sockno);
  298.                                         sendok = 0;
  299.                                         recv_start = 0;
  300.                                         if(local_port > 65400)
  301.                                         {
  302.                                                 local_port = 60000;
  303.                                         }
  304.                                         socket(sockno, Sn_MR_TCP, local_port++, Sn_MR_ND);
  305.                         break;
  306.                 }
  307.                
  308.                 cnt ++;
  309.                 if(cnt >= timeout_ms)
  310.                 {
  311.                         log_net("20");
  312.                         return 1;//connect timeout
  313.                 }
  314.                 delay_ms(1);
  315.         }
  316. }

  317. u8 func_analysis_http_download_header(u8* buffer, u16 len_buf, u8 *resp_code, u8 *is_stream, u32 *cont_len)
  318. {
  319.         *resp_code = 0;
  320.         *is_stream = 0;
  321.         *cont_len = 0;
  322.         if(strstr((char*)buffer, "\r\n\r\n") != NULL)//response header ok
  323.         {
  324.                 char *p1, *p2;
  325.                
  326.                 p1 = strstr((char*)buffer, "HTTP/1.1 200");       
  327.                 if(p1 != NULL)// io stream
  328.                 {
  329.                         *resp_code = 200;
  330.                 }               
  331.                
  332.                 p1 = strstr((char*)buffer, "Accept-Ranges: bytes");       
  333.                 if(p1 != NULL)// io stream
  334.                 {
  335.                         *is_stream = 1;
  336.                 }
  337.                
  338.                 p1 = strstr((char*)buffer, "Content-Length: ");                                                                               
  339.                 if(p1 != NULL)
  340.                 {
  341.                         p2 = strstr(p1, "\r\n");
  342.                         if(p2 != NULL)
  343.                         {
  344.                                 char str_len[8] = {0,};
  345.                                 memcpy(str_len, p1 + strlen("Content-Length: "), p2 - p1 - strlen("Content-Length: "));
  346.                                 *cont_len = atoi(str_len);
  347.                                 return 0;
  348.                         }
  349.                 }
  350.         }
  351.         return 1;
  352. }

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

  359. void func_pack_http_get_download_header(u8* buff, u16 size_buff, u8* srv_ip, u16 srv_port, char* interface)
  360. {
  361.         u8 tmp[64];
  362.         memset(buff, 0, size_buff);
  363.         //header
  364.         memcpy(buff, "GET ", strlen("GET "));
  365.         strcat((char *)buff, interface);
  366.         strcat((char *)buff, " HTTP/1.1\r\n");
  367.         memset(tmp, 0 , sizeof(tmp));
  368.         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);
  369.         strcat((char *)buff, (char*)tmp);
  370.         strcat((char *)buff, "Connection: Keep-Alive\r\n");
  371.         strcat((char *)buff, "User-Agent: W5500\r\n");
  372.         strcat((char *)buff, "Accept-Encoding: gzip,deflate\r\n");
  373.         strcat((char *)buff, "\r\n");
  374. }
 楼主| 初级工程渣 发表于 2023-1-15 19:16 | 显示全部楼层
最后,附上我测试的效果图:
7139763c3e07cda283.png
 楼主| 初级工程渣 发表于 2023-1-15 19:17 | 显示全部楼层
串口打印的字节数和 Content-Length的长度一致,并和原bin文件做了对比,内容一致。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

72

主题

816

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部