基于正点原子阿波罗F429开发板的LWIP应用(4)——HTTP Server功能

说在开头

        正点原子F429开发板主芯片采用的是STM32F429IGT6,网络PHY芯片采用的是LAN8720A(V1)和YT8512C(V2),采用的是RMII连接,PHY_ADDR为0;在代码中将会对不同的芯片做出适配。

        CubeMX版本:6.6.1;

        F4芯片组Pack包版本:STM32Cube FW_F4 V1.27.0;

       

1、实现简单的http服务端

        本实验代码以《基于正点原子阿波罗F429开发板的LWIP应用(3)——Netbiosns功能》一章的代码作为基础;

  • 打开"Middlewares/Third_Party/LwIP/src/apps/http文件夹,仅保留“httpd.c、fsdata.c、fsdata.h、fs.c、httpd_structs.h”五个文件(不删除也行,就是看着有点碍事);
  • 打开"\Middlewares\Third_Party\LwIP\src\include\lwip\apps文件夹,删除“altcp_proxyconnect.h”和“http_client.h”两个文件(不删除也行,就是看着有点碍事);
  • MDK新增http分组,将"httpd.c"和“fs.c”添加到http分组中,并将"Middlewares/Third_Party/LwIP/src/apps/http文件夹添加到编译路径(fsdata.h文件)
  • main.c开头添加头文件:#include "lwip/apps/httpd.h"
  • main函数while(1)前增加以下代码:httpd_init();

        至此就修改完成了,编译0警告0错误,下载后在浏览器访问板子IP就可以看到可以打开网页了。(注意:有部分国产单片机需要在lwipopts.h文件中添加“ #define HTTP_IS_DATA_VOLATILE(hs) TCP_WRITE_FLAG_COPY ”宏定义才可以正常打开网页)

如果要查看http服务端运行时的log,可以在lwipopts.h文件中添加以下内容打开log输出:

#define LWIP_DEBUG
#define HTTPD_DEBUG                   LWIP_DBG_ON

2、实现SSI和CGI功能

2.1、CGI技术介绍  

        公共网关接口 CGI(Common Gateway Interface) 是 WWW 技术中最重要的技术之一,有着不可替代的重要地位。CGI 是外部应用程序与 Web 服务器之间的接口标准,是在 CGI 程序和Web服务器之间传递信息的规程。CGI 规范允许 Web 服务器执行外部程序,并将它们的输出发送给Web 浏览器,CGI 在物理上是一段程序,运行在服务器上,提供同客户端 HTML 页面的接口。

        绝大多数的 CGI 程序被用来解释处理来自表单的输入信息,并在服务器产生相应的处理, 或将相应的信息反馈给浏览器,CGI 程序使网页具有交互功能。在我们本章实验中我们通过浏览器控制开发板上的 LED 和蜂鸣器就是使用的 CGI 技术。

2.2、SSI技术介绍

        服务器端嵌入:Server Side Include,是一种类似于 ASP 的基于服务器的网页制作技术。大多数的 WEB 服务器等均支持 SSI 命令。将内容发送到浏览器之前,可以使用“服务器端包含 (SSI)”指令将文本、图形或应用程序信息包含到网页中。因为包含 SSI 指令的文件要求特殊处理,所以必须为所有 SSI 文件赋予 SSI 文件扩展名。默认扩展名是 .stm、.shtm 和 .shtml。

        SSI 是为 WEB 服务器提供的一套命令,这些命令只要直接嵌入到 HTML 文档的注释内容 之中即可。如:<!--#include file="info.htm"-->就是一条 SSI 指令,其作用是将"info.htm"的内容拷贝到当前的页面中,当访问者来浏览时,会看到其它 HTML 文档一样显示 info.htm 其中的内容。其它的 SSI 指令使用形式基本同刚才的举例差不多,可见 SSI 使用只是插入一点代码而已,使用形式非常简单。<!-- -->是 HTML 语法中表示注释,当 WEB 服务器不支持 SSI 时,会忽略这些信息。

2.3、网页数组制作

        在嵌入式开发中网页不是以文件的形式保存的,而是以网页数组的形式保存在“fsdata.c”文件中。正点原子自己开发了一个网页数组制作工具:makefsdata.exe,使用方法如下:

        首先将网页源文件放到makefsdata文件夹中的fd文件夹中(本实验用到的网页源文件会放在实验源码中);

        进入makefsdata文件夹,在上面的路径栏输入cmd后敲回车打开cmd窗口;

        在命令行输入“makefsdata -i”后敲回车,可以看到命令行有提示有哪些文件被制作成了网页数组,并且makefsdata文件夹中多了一个fsdata.c文件。操作如下图:

2.4 SSI功能实现

        本实验代码在《实现简单的http服务端》章节代码的基础上修改。

  • 将刚才生成的网页数组文件“fsdata.c”文件替换"Middlewares/Third_Party/LwIP/src/apps/http”文件夹中的同名文件。
  • 在“lwipopts.h”文件中添加以下内容:“#define LWIP_HTTPD_SSI            1”
  • 在“LWIP/APP”中新建2个文件:“http.c”和“http.h”,分别粘贴以下代码:
  • /*http.c代码*/
    #include "http.h"/************************SSI相关**************************************/#define NUM_CONFIG_SSI_TAGS     4       /* SSI的TAG数量 */static const char *ppcTAGs[] = /* SSI的Tag */
    {"t", /* ADC值 */"w", /* 温度值 */"h", /* 时间 */"y"  /* 日期 */
    };/*** @breif       SSIHandler中需要用到的处理ADC的函数* @param       pcInsert    :* @retval      无*/
    void ADC_Handler(char *pcInsert)
    {/* 准备添加到html中的数据 */*pcInsert       = (char)(1 + 0x30);*(pcInsert + 1) = (char)(2 + 0x30);*(pcInsert + 2) = (char)(3 + 0x30);*(pcInsert + 3) = (char)(4 + 0x30);
    }/*** @breif       SSIHandler中需要用到的处理内部温度传感器的函数* @param       pcInsert    :* @retval      无*/
    void Temperate_Handler(char *pcInsert)
    {/* 添加到html中的数据 */*pcInsert = (char)(0 + 0x30);*(pcInsert + 1) = (char)(3 + 0x30);*(pcInsert + 2) = (char)(3 + 0x30);*(pcInsert + 3) = '.';*(pcInsert + 4) = (char)(3 + 0x30);*(pcInsert + 5) = (char)(3 + 0x30);
    }/*** @breif       SSIHandler中需要用到的处理RTC时间的函数* @param       pcInsert    :* @retval      无*/
    void RTCTime_Handler(char *pcInsert)
    {uint8_t hour, min, sec;hour = 9;min = 35;sec = 20;*pcInsert = (char)((hour / 10) + 0x30);*(pcInsert + 1) = (char)((hour % 10) + 0x30);*(pcInsert + 2) = ':';*(pcInsert + 3) = (char)((min / 10) + 0x30);*(pcInsert + 4) = (char)((min % 10) + 0x30);*(pcInsert + 5) = ':';*(pcInsert + 6) = (char)((sec / 10) + 0x30);*(pcInsert + 7) = (char)((sec % 10) + 0x30);
    }/*** @breif       SSIHandler中需要用到的处理RTC日期的函数* @param       pcInsert    :* @retval      无*/
    void RTCdate_Handler(char *pcInsert)
    {uint8_t year, month, date, week;year =24;month = 8;date = 2;week = 1;*pcInsert = '2';*(pcInsert + 1) = '0';*(pcInsert + 2) = (char)((year / 10) + 0x30);*(pcInsert + 3) = (char)((year % 10) + 0x30);*(pcInsert + 4) = '-';*(pcInsert + 5) = (char)((month / 10) + 0x30);*(pcInsert + 6) = (char)((month % 10) + 0x30);*(pcInsert + 7) = '-';*(pcInsert + 8) = (char)((date / 10) + 0x30);*(pcInsert + 9) = (char)((date % 10) + 0x30);*(pcInsert + 10) = ' ';*(pcInsert + 11) = 'w';*(pcInsert + 12) = 'e';*(pcInsert + 13) = 'e';*(pcInsert + 14) = 'k';*(pcInsert + 15) = ':';*(pcInsert + 16) = (char)(week + 0x30);
    }/*** @breif       SSI的Handler句柄* @param       iIndex      :* @param       pcInsert    :* @param       iInsertLen  :* @retval      无*/
    static u16_t SSIHandler(int iIndex, char *pcInsert, int iInsertLen)
    {switch (iIndex){case 0:ADC_Handler(pcInsert);break;case 1:Temperate_Handler(pcInsert);break;case 2:RTCTime_Handler(pcInsert);break;case 3:RTCdate_Handler(pcInsert);break;}return strlen(pcInsert);
    }/****************************************************************************/void http_Init(void)
    {httpd_init();http_set_ssi_handler(SSIHandler, ppcTAGs, NUM_CONFIG_SSI_TAGS);   /* 配置SSI句柄 */}
    /*http.h文件代码*/
    #ifndef __HTTP_H__
    #define __HTTP_H__#include "main.h"#include "lwip/apps/httpd.h"#define READ_LED1 HAL_GPIO_ReadPin(LED0_GPIO_Port, LED0_Pin)
    #define READ_BEEP HAL_GPIO_ReadPin(LED1_GPIO_Port, LED1_Pin)void http_Init(void);#endif
    
  • 将“http.c”文件添加到工程的http分组中
  • 将“Middlewares\Third_Party\LwIP\src\apps\http\httpd.c”文件进行如下修改(注意:此处修改是博主在做项目时遇到问题做出的修改,如果你只是为了跑实验则可以不改)
  • /*get_tag_insert函数修改成如下内容*/
    #if LWIP_HTTPD_SSI
    /*** Insert a tag (found in an shtml in the form of "<!--#tagname-->" into the file.* The tag's name is stored in ssi->tag_name (NULL-terminated), the replacement* should be written to hs->tag_insert (up to a length of LWIP_HTTPD_MAX_TAG_INSERT_LEN).* The amount of data written is stored to ssi->tag_insert_len.** @todo: return tag_insert_len - maybe it can be removed from struct http_state?** @param hs http connection state*/
    static void
    get_tag_insert(struct http_state *hs)
    {
    #if LWIP_HTTPD_SSI_RAWconst char *tag;
    #else /* LWIP_HTTPD_SSI_RAW */int tag;
    #endif /* LWIP_HTTPD_SSI_RAW */
    //  size_t len;struct http_ssi_state *ssi;
    #if LWIP_HTTPD_SSI_MULTIPARTu16_t current_tag_part;
    #endif /* LWIP_HTTPD_SSI_MULTIPART */LWIP_ASSERT("hs != NULL", hs != NULL);ssi = hs->ssi;LWIP_ASSERT("ssi != NULL", ssi != NULL);
    #if LWIP_HTTPD_SSI_MULTIPARTcurrent_tag_part = ssi->tag_part;ssi->tag_part = HTTPD_LAST_TAG_PART;
    #endif /* LWIP_HTTPD_SSI_MULTIPART */
    #if LWIP_HTTPD_SSI_RAWtag = ssi->tag_name;
    #endif/*这行代码的作用是清空SSI缓冲区,防止对下一次SSI请求产生干扰*/memset(ssi->tag_insert,NULL,LWIP_HTTPD_MAX_TAG_INSERT_LEN+1);if (httpd_ssi_handler
    #if !LWIP_HTTPD_SSI_RAW&& httpd_tags && httpd_num_tags
    #endif /* !LWIP_HTTPD_SSI_RAW */) {/* Find this tag in the list we have been provided. */
    #if LWIP_HTTPD_SSI_RAW{
    #else /* LWIP_HTTPD_SSI_RAW */for (tag = 0; tag < httpd_num_tags; tag++) {if (strcmp(ssi->tag_name, httpd_tags[tag]) == 0)
    #endif /* LWIP_HTTPD_SSI_RAW */{ssi->tag_insert_len = httpd_ssi_handler(tag, ssi->tag_insert,LWIP_HTTPD_MAX_TAG_INSERT_LEN
    #if LWIP_HTTPD_SSI_MULTIPART, current_tag_part, &ssi->tag_part
    #endif /* LWIP_HTTPD_SSI_MULTIPART */
    #if LWIP_HTTPD_FILE_STATE, (hs->handle ? hs->handle->state : NULL)
    #endif /* LWIP_HTTPD_FILE_STATE */);
    #if LWIP_HTTPD_SSI_RAWif (ssi->tag_insert_len != HTTPD_SSI_TAG_UNKNOWN)
    #endif /* LWIP_HTTPD_SSI_RAW */{return;}}}}/*以下代码是在识别到的未知的SSI标签的位置插入“<b>***UNKNOWN TAG ***</b>”,屏蔽后将不替换*///  /* If we drop out, we were asked to serve a page which contains tags that
    //   * we don't have a handler for. Merely echo back the tags with an error
    //   * marker. */
    //#define UNKNOWN_TAG1_TEXT "<b>***UNKNOWN TAG "
    //#define UNKNOWN_TAG1_LEN  18
    //#define UNKNOWN_TAG2_TEXT "***</b>"
    //#define UNKNOWN_TAG2_LEN  7
    //  len = LWIP_MIN(sizeof(ssi->tag_name), LWIP_MIN(strlen(ssi->tag_name),
    //                 LWIP_HTTPD_MAX_TAG_INSERT_LEN - (UNKNOWN_TAG1_LEN + UNKNOWN_TAG2_LEN)));
    //  MEMCPY(ssi->tag_insert, UNKNOWN_TAG1_TEXT, UNKNOWN_TAG1_LEN);
    //  MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN], ssi->tag_name, len);
    //  MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN + len], UNKNOWN_TAG2_TEXT, UNKNOWN_TAG2_LEN);
    //  ssi->tag_insert[UNKNOWN_TAG1_LEN + len + UNKNOWN_TAG2_LEN] = 0;//  len = strlen(ssi->tag_insert);
    //  LWIP_ASSERT("len <= 0xffff", len <= 0xffff);
    //  ssi->tag_insert_len = (u16_t)len;
    }
    #endif /* LWIP_HTTPD_SSI *//*http_find_file函数修改成如下内容*/
    static err_t
    http_find_file(struct http_state *hs, const char *uri, int is_09)
    {size_t loop;struct fs_file *file = NULL;char *params = NULL;err_t err;
    #if LWIP_HTTPD_CGIint i;
    #endif /* LWIP_HTTPD_CGI */
    #if !LWIP_HTTPD_SSIconst
    #endif /* !LWIP_HTTPD_SSI *//* By default, assume we will not be processing server-side-includes tags */u8_t tag_check = 0;/* Have we been asked for the default file (in root or a directory) ? */
    #if LWIP_HTTPD_MAX_REQUEST_URI_LENsize_t uri_len = strlen(uri);if ((uri_len > 0) && (uri[uri_len - 1] == '/') &&((uri != http_uri_buf) || (uri_len == 1))) {size_t copy_len = LWIP_MIN(sizeof(http_uri_buf) - 1, uri_len - 1);if (copy_len > 0) {MEMCPY(http_uri_buf, uri, copy_len);http_uri_buf[copy_len] = 0;}
    #else /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */if ((uri[0] == '/') &&  (uri[1] == 0)) {
    #endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN *//* Try each of the configured default filenames until we find onethat exists. */for (loop = 0; loop < NUM_DEFAULT_FILENAMES; loop++) {const char *file_name;
    #if LWIP_HTTPD_MAX_REQUEST_URI_LENif (copy_len > 0) {size_t len_left = sizeof(http_uri_buf) - copy_len - 1;if (len_left > 0) {size_t name_len = strlen(httpd_default_filenames[loop].name);//index.htmlsize_t name_copy_len = LWIP_MIN(len_left, name_len);MEMCPY(&http_uri_buf[copy_len], httpd_default_filenames[loop].name, name_copy_len);http_uri_buf[copy_len + name_copy_len] = 0;}file_name = http_uri_buf;} else
    #endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */{file_name = httpd_default_filenames[loop].name;//文件名赋值}LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Looking for %s...\n", file_name));err = fs_open(&hs->file_handle, file_name);//打开文件 file_name储存要打开文件的名称if (err == ERR_OK) {uri = file_name;file = &hs->file_handle;LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opened.\n"));
    #if LWIP_HTTPD_SSItag_check = httpd_default_filenames[loop].shtml;
    #endif /* LWIP_HTTPD_SSI */break;}}}if (file == NULL) {/* No - we've been asked for a specific file. *//* First, isolate the base URI (without any parameters) */params = (char *)strchr(uri, '?');if (params != NULL) {/* URI contains parameters. NULL-terminate the base URI */*params = '\0';params++;}#if LWIP_HTTPD_CGIhttp_cgi_paramcount = -1;/* Does the base URI we have isolated correspond to a CGI handler? */if (httpd_num_cgis && httpd_cgis) {for (i = 0; i < httpd_num_cgis; i++) {if (strcmp(uri, httpd_cgis[i].pcCGIName) == 0) {/** We found a CGI that handles this URI so extract the* parameters and call the handler.*/
    //          http_cgi_paramcount = extract_uri_parameters(hs, params);
    //          uri = httpd_cgis[i].pfnCGIHandler(i, http_cgi_paramcount, hs->params,
    //                                         hs->param_vals);char date[strlen(params)];strcpy(date,params);http_cgi_paramcount = extract_uri_parameters(hs, params);uri = httpd_cgis[i].pfnCGIHandler(i, http_cgi_paramcount, hs->params,hs->param_vals, date);break;}}}
    #endif /* LWIP_HTTPD_CGI */LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opening %s\n", uri));err = fs_open(&hs->file_handle, uri);//打开文件名 uri储存要打开文件的名称if (err == ERR_OK) {file = &hs->file_handle;} else {file = http_get_404_file(hs, &uri);}
    #if LWIP_HTTPD_SSIif (file != NULL) {if (file->flags & FS_FILE_FLAGS_SSI) {tag_check = 1;} else {
    #if LWIP_HTTPD_SSI_BY_FILE_EXTENSIONtag_check = http_uri_is_ssi(file, uri);
    #endif /* LWIP_HTTPD_SSI_BY_FILE_EXTENSION */}}
    #endif /* LWIP_HTTPD_SSI */}if (file == NULL) {/* None of the default filenames exist so send back a 404 page */file = http_get_404_file(hs, &uri);}return http_init_file(hs, file, is_09, uri, tag_check, params);
    }

  • main.c开头的“#include "lwip/apps/httpd.h" ”替换成“#include "http.h" ”
  • main函数while(1)前的“httpd_init(); ”替换成“http_Init(); ”

        至此就修改完成了,编译0警告0错误,下载后在浏览器访问板子IP就可以看到可以打开网页,之后进入网页中的“ADC/内部温度传感器”或者“RTC实时时钟”页面可以看到有数值,里面的内容就是“ADC_Handler”、“Temperate_Handler”、“RTCTime_Handler”、和“RTCdate_Handler”这四个函数中的值,这里博主就没有编写ADC、RTC的驱动,只用了几个固定值代替,读者可以自行下载源代码,修改其中的值编译测试。

2.5 CGI功能实现

         本实验代码在《SSI功能实现》章节代码的基础上修改。

  • 在“lwipopts.h”文件中添加以下内容:“#define LWIP_HTTPD_CGI            1”
  • 在"http.c"文件“http_Init”函数前增加以下内容:
  • /************************CGI相关**************************************/#define NUM_CONFIG_CGI_URIS     2       /* CGI的URI数量 *//* 控制LED和BEEP的CGI handler */
    const char *LEDS_CGI_Handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[],char *hotel_str);
    const char *BEEP_CGI_Handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[],char *hotel_str);static const tCGI ppcURLs[] = /* cgi程序 */
    {{"/leds.cgi", LEDS_CGI_Handler},{"/beep.cgi", BEEP_CGI_Handler},
    };/*** @breif       当web客户端请求浏览器的时候,使用此函数被CGI handler调用* @param       pcToFind    :* @param       pcParam     :* @param       iNumParams  :* @retval      无*/
    static int FindCGIParameter(const char *pcToFind, char *pcParam[], int iNumParams)
    {int iLoop;for (iLoop = 0; iLoop < iNumParams; iLoop++){if (strcmp(pcToFind, pcParam[iLoop]) == 0){return (iLoop);     /* 返回iLOOP */}}return (-1);
    }/*** @breif       CGI LED控制句柄* @param       iIndex      : CGI句柄索引号* @param       iNumParams  :* @param       pcParam     :* @param       pcValue     :* @retval      无*/
    const char *LEDS_CGI_Handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[],char *hotel_str)
    {uint8_t i = 0; /* 注意根据自己的GET的参数的多少来选择i值范围 */iIndex = FindCGIParameter("LED1", pcParam, iNumParams);   /* 找到led的索引号 *//* 只有一个CGI句柄 iIndex=0 */if (iIndex != -1){
    //        HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,GPIO_PIN_SET);
    //				HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET);for (i = 0; i < iNumParams; i++)                /* 检查CGI参数: example GET /leds.cgi?led=2&led=4 */{if (strcmp(pcParam[i], "LED1") == 0)        /* 检查参数"led" */{if (strcmp(pcValue[i], "LED1ON") == 0)  /* 改变LED1状态 */{HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET);    /* 打开LED1 */}else if (strcmp(pcValue[i], "LED1OFF") == 0){HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_SET);    /* 关闭LED1 */}}}}if (READ_LED1 == 0 && READ_BEEP == 0){return "/STM32F407LED_ON_BEEP_ON.shtml";    /* LED1开,BEEP开 */}else if (READ_LED1 == 0 && READ_BEEP == 1){return "/STM32F407LED_ON_BEEP_OFF.shtml";   /* LED1开,BEEP关 */}else if (READ_LED1 == 1 && READ_BEEP == 1){return "/STM32F407LED_OFF_BEEP_OFF.shtml";  /*  LED1关,BEEP关 */}else{return "/STM32F407LED_OFF_BEEP_ON.shtml";   /* LED1关,BEEP开 */}
    }/*** @breif       BEEP的CGI控制句柄* @param       iIndex      : CGI句柄索引号* @param       iNumParams  :* @param       pcParam     :* @param       pcValue     :* @retval      无*/
    const char *BEEP_CGI_Handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[],char *hotel_str)
    {uint8_t i = 0;iIndex = FindCGIParameter("BEEP", pcParam, iNumParams);   /* 找到BEEP的索引号 */if (iIndex != -1)   /* 找到BEEP索引号 */{
    //        HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,GPIO_PIN_SET);
    //				HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET);			for (i = 0; i < iNumParams; i++){if (strcmp(pcParam[i], "BEEP") == 0)             /* 查找CGI参数 */{if (strcmp(pcValue[i], "BEEPON") == 0)       /* 打开BEEP */{HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);}else if (strcmp(pcValue[i], "BEEPOFF") == 0) /* 关闭BEEP */{HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);}}}}if (READ_LED1 == 0 && READ_BEEP == 0){return "/STM32F407LED_ON_BEEP_ON.shtml";    /* LED1开,BEEP开 */}else if (READ_LED1 == 0 && READ_BEEP == 1){return "/STM32F407LED_ON_BEEP_OFF.shtml";   /* LED1开,BEEP关 */}else if (READ_LED1 == 1 && READ_BEEP == 1){return "/STM32F407LED_OFF_BEEP_OFF.shtml";  /*  LED1关,BEEP关 */}else{return "/STM32F407LED_OFF_BEEP_ON.shtml";   /* LED1关,BEEP开 */}
    }/****************************************************************************/
  • 在"http.c"文件“http_Init”函数最后增加以下内容:
  • http_set_cgi_handlers(ppcURLs, NUM_CONFIG_CGI_URIS);      /* 配置CGI句柄 */
  • 注销“stm32f4xx_it.c”中关于LED反转的内容;
  • main函数“http_Init();”前添加以下内容:
  • 	HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,GPIO_PIN_SET);HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET);
  • 将“Middlewares\Third_Party\LwIP\src\include\lwip\apps\httpd.h”文件中关于“tCGIHandler”的定义修改成如下内容:
  • //typedef const char *(*tCGIHandler)(int iIndex, int iNumParams, char *pcParam[],
    //                             char *pcValue[]);
    /*新增加的参数表示CGI请求的具体内容,可在CGI请求处理函数中通过printf打印具体的请求内容*/
    typedef const char *(*tCGIHandler)(int iIndex, int iNumParams, char *pcParam[],char *pcValue[], char *hotel_str);

至此就修改完成了,编译0警告0错误,下载后在浏览器访问板子IP就可以看到可以打开网页,之后进入网页中的“LED/BEEP控制”就可以通过按钮控制板子上的2个LED。

3、实现网页功能

        本实验代码在《CGI功能实现》章节代码的基础上修改。

  • 在“lwipopts.h”文件中添加以下内容:“#define LWIP_HTTPD_SUPPORT_POST               1”
  • http.c文件最后添加以下内容:
  • /************************************HTTP POST	请求*************************************************/
    uint8_t login_flag = 0;/* 开始处理http post请求*/
    err_t httpd_post_begin(void *connection, const char *uri, const char *http_request, u16_t http_request_len, int content_len, char *response_uri, u16_t response_uri_len, u8_t *post_auto_wnd)
    {/*http_request是http header的全部内容,http_request_len是http header的总长度,content_len是全部表单内容的总长度。		*/printf("[httpd_post_begin] connection=0x%p, uri=%s\n", connection, uri);//打印请求的内容//printf("%.*s\n", http_request_len, http_request);//打印完整的POST请求if (strcmp(uri, "/checklogin.cgi") == 0)/*如果是提交登录信息*/{		return ERR_OK;}return ERR_ARG;
    }char *POST_rec;/* 接收表单数据 */
    err_t httpd_post_receive_data(void *connection, struct pbuf *p)
    {int len;struct pbuf *q;char *p1, *p2;printf("[httpd_post_receive_data] connection=0x%p, payload=0x%p, len=%d\n", connection, p->payload, p->tot_len);for (q = p; q != NULL; q = q->next)printf("\n%.*s", q->len, (char *)q->payload);len = len + p->len;printf("\n");printf("\n密码判断\n");p1  = strstr(p->payload, "username=");p2 = strstr(p->payload, "&login=");if( (p1 != NULL)&&(p2 != NULL) ){char user_login[p2-p1];char local_info[50] =  "";memcpy(user_login, p1, (p2-p1));sprintf(local_info,"username=%s&password=%s","stm32","stm32");//printf("\nuser_login:%s\n",user_login);//printf("\nlocal_info:%s\n",local_info);if( ((p2-p1)==strlen(local_info))&&(strncmp(p1,local_info,strlen(local_info)) == 0) ){login_flag = 1;printf("\n账号密码正确\n");}else{printf("\n账号密码错误\n");}}	return ERR_OK;
    }/* 结束处理http post请求*/
    void httpd_post_finished(void *connection, char *response_uri, u16_t response_uri_len)
    {/*没有对response_uri字符数组赋值的话,最终浏览器显示的页面为404错误页面,提示找不到网页。如果对response_uri赋了值,那么就显示response_uri字符串指定的网页。*///strlcpy(response_uri, "/success.html", response_uri_len);//printf("[httpd_post_finished] connection=0x%p\n", connection);if(login_flag) strlcpy(response_uri, "/index.html",response_uri_len);else strlcpy(response_uri, "/login.html",response_uri_len);
    }
    /************************************HTTP POST	请求*************************************************/
    
  • 将“Middlewares\Third_Party\LwIP\src\apps\http\httpd.c”文件进行如下修改:
  • /*开头增加外部变量声明*/
    extern uint8_t login_flag;/*将http_find_file函数最后一句“return http_init_file(hs, file, is_09, uri, tag_check, params);”替换成如下内容*///  return http_init_file(hs, file, is_09, uri, tag_check, params);if(login_flag){return http_init_file(hs, file, is_09, uri, tag_check, params);}else{uri = "/login.html";fs_open(&hs->file_handle, uri);//´ò¿ªµÇ¼ҳ//printf("δµÇ¼\n");return http_init_file(hs, file, is_09, uri, tag_check, params);}
  • main函数“http_Init();”前添加以下内容:
  • 注销“stm32f4xx_it.c”中关于LED反转的内容;
  • main函数“http_Init();”前添加以下内容:
  • 注销“stm32f4xx_it.c”中关于LED反转的内容;
  • main函数“http_Init();”前添加以下内容:

至此就修改完成了,编译0警告0错误,下载后在浏览器访问板子IP就可以看到登录页面,登录的用户名和密码都是“stm32”,登录成功后就可以看到《CGI功能实现》章节的内容了。同时串口也会打印登录过程信息:

  如果要修改登录的用户名和密码仅需修改“http.c”中的“httpd_post_receive_data”以下内容即可:

最后,本次实验的内容就到此结束了,简单的向大家介绍了STM32+LWIP实现http server的过程,本章节实验源码、网页源文件、网页数组生成工具均在此:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/82656.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计模式-结构型模式(详解)

适配器模式 将一个类的接口转换成客户端期望的另一个接口&#xff0c;解决接口不兼容问题。 适配器模式由四部分组成&#xff1a; 客户端&#xff1a;即需要使用目标接口的类 目标接口 需要适配的类&#xff0c;也就是已经存在好的功能&#xff0c;但客户端通过目标接口没办…

银河麒麟操作系统下载

产品试用申请国产操作系统、麒麟操作系统——麒麟软件官方网站 下载页面链接如上&#xff0c;申请试用即可。 申请试用填写后提交&#xff0c;界面就变成了这样&#xff0c;可以挑选适合自己的版本。 海思麒麟9006C版&#xff0c;如下&#xff1a; 本地下载&#xff1a;Kylin…

[CARLA系列--03]如何打包生成CARLA 0.9.15的非编辑版(地图的加载与卸载)

前两篇文章介绍了如何去安装可编辑版的CARLA 0.9.15&#xff0c;这个完整的工程文件实在是太大了&#xff0c;大概消耗了100个G的磁盘空间&#xff0c;当在进行一个CARLA项目的时候&#xff0c;不利于在每个开发电脑都去安装部署一套CARLA 0.9.15的源码&#xff0c;所以把自己这…

【机器学习基础】机器学习入门核心算法:朴素贝叶斯(Naive Bayes)

机器学习入门核心算法&#xff1a;朴素贝叶斯&#xff08;Naive Bayes&#xff09;&#xff09; 一、算法逻辑1.1 基本概念1.2 基本流程 二、算法原理与数学推导2.1 贝叶斯定理2.2 朴素贝叶斯分类器2.3 不同分布假设下的概率计算2.3.1 高斯朴素贝叶斯&#xff08;连续特征&…

云服务器系统盘满了,但是其他正常,是否可能是被攻击了

目录 问题背景分析解决系统盘满的问题解决结果 问题背景 今天登录我的云服务器看了眼&#xff0c;发现系统盘满了&#xff0c;但是其他正常 分析 1、首先要确认是否是被攻击&#xff1a; top / htop (安装&#xff1a;yum install htop 或 apt install htop)&#xff1a;…

双因子COX 交互 共线性 -spss

SPSS 简要界面操作步骤(针对双因子 COX 分析) 1. 数据准备 变量格式:确保数据已整理为以下格式(示例): 时间变量(如 Time_to_Recurrence)结局变量(如 Recurrence:1=复发,0=未复发)预测变量(CSPG4_HSCORE、FAM49B_Status 二分类变量)协变量(如 Lesion_Size、Pat…

【MySQL】第12节|MySQL 8.0 主从复制原理分析与实战(二)

一、组复制&#xff08;MGR&#xff09;核心概念 1. 定义与定位 目标&#xff1a;解决传统主从复制的单点故障、数据不一致问题&#xff0c;提供高可用、高扩展的分布式数据库方案。基于 GTID&#xff1a;依赖全局事务标识符&#xff08;GTID&#xff09;实现事务一致性&…

React 泛型组件:用TS来打造灵活的组件。

文章目录 前言一、什么是泛型组件&#xff1f;二、为什么需要泛型组件&#xff1f;三、如何在 React 中定义泛型组件&#xff1f;基础泛型组件示例使用泛型组件 四、泛型组件的高级用法带默认类型的泛型组件多个泛型参数 五、泛型组件的实际应用场景数据展示组件表单组件状态管…

如何手搓一个查询天气的mcp server

环境配置烦请移步上一篇博客 这里直接步入主题&#xff0c;天气查询的api用的是openweather&#xff0c;免费注册就可以使用了 每天1000次内使用时免费的&#xff0c;大概的api 如下 https://api.openweathermap.org/data/2.5/weather?qBeijing,cn&APPID注册后可以拿到一个…

深入解析计算机网络核心协议:ARP、DHCP、DNS与HTTP

文章目录 一、ARP&#xff08;地址解析协议&#xff09;1.1 定义与功能1.2 工作原理1.3 应用场景1.4 安全风险与防御 二、DHCP&#xff08;动态主机配置协议&#xff09;2.1 定义与功能2.2 工作原理2.3 应用场景2.4 优缺点与安全建议 三、DNS&#xff08;域名系统&#xff09;3…

《Java 单例模式:从类加载机制到高并发设计的深度技术剖析》

【作者简介】“琢磨先生”--资深系统架构师、985高校计算机硕士&#xff0c;长期从事大中型软件开发和技术研究&#xff0c;每天分享Java硬核知识和主流工程技术&#xff0c;欢迎点赞收藏&#xff01; 一、单例模式的核心概念与设计目标 在软件开发中&#xff0c;我们经常会遇…

NL2SQL代表,Vanna

Vanna 核心功能、应用场景与技术特性详解 一、核心功能 1. 自然语言转SQL查询 Vanna 允许用户通过自然语言提问&#xff08;如“显示2024年销售额最高的产品”&#xff09;&#xff0c;自动生成符合数据库规范的SQL查询语句。其底层采用 RAG&#xff08;检索增强生成&#xf…

【动态规划】子数组系列(二)

&#x1f4dd;前言说明&#xff1a; 本专栏主要记录本人的动态规划算法学习以及LeetCode刷题记录&#xff0c;按专题划分每题主要记录&#xff1a;&#xff08;1&#xff09;本人解法 本人屎山代码&#xff1b;&#xff08;2&#xff09;优质解法 优质代码&#xff1b;&…

68元开发板,开启智能硬件新篇章——明远智睿SSD2351深度解析

在智能硬件开发领域&#xff0c;开发板的选择至关重要。它不仅关系到项目的开发效率&#xff0c;还直接影响到最终产品的性能与稳定性。而今天&#xff0c;我要为大家介绍的这款明远智睿SSD2351开发板&#xff0c;仅需68元&#xff0c;却拥有远超同价位产品的性能与功能&#x…

篇章六 数据结构——链表(二)

目录 1. LinkedList的模拟实现 1.1 双向链表结构图​编辑 1.2 三个简单方法的实现 1.3 头插法 1.4 尾插法 1.5 中间插入 1.6 删除 key 1.7 删除所有key 1.8 clear 2.LinkedList的使用 2.1 什么是LinkedList 5.2 LinkedList的使用 1.LinkedList的构造 2. LinkedList的…

删除队列中整数

给定一个长度为N的整数数列A_1,A_2,...,A_N&#xff0c;请重复以下操作K次。 每次选择数列中最小的整数&#xff08;如果最小值不止一个&#xff0c;选择最靠前的&#xff09;&#xff0c;将其删除&#xff0c;并把与它相邻的整数加上被删除的数值。 请问K次操作后的序列是什…

[神经网络]使用olivettiface数据集进行训练并优化,观察对比loss结果

结合归一化和正则化来优化网络模型结构&#xff0c;观察对比loss结果 搭建的神经网络&#xff0c;使用olivettiface数据集进行训练&#xff0c;结合归一化和正则化来优化网络模型结构&#xff0c;观察对比loss结果 from sklearn.datasets import fetch_olivetti_faces #倒入数…

算法分析·回溯法

回溯法 方法概述算法框架问题实例TSP 问题n皇后问题 回溯法效率分析 方法概述 回溯法是一个既带有系统性又带有跳跃性的搜索算法&#xff1b; **系统性&#xff1a;**它在包含问题的所有解的解空间树中&#xff0c;按照深度优先的策略&#xff0c;从根结点出发搜索解空间树。…

Golang分布式系统开发实践指南

Golang分布式系统开发实践指南 一、为什么选择Golang&#xff1f; ​原生并发模型​ Goroutine和Channel机制天然适合分布式系统的并发需求​高性能编译​ 静态编译生成二进制文件&#xff0c;部署简单&#xff0c;内存占用低​丰富生态​ Go Module管理、标准库支持HTTP/2、…

基于stm32风速风向温湿度和瓦斯检测(仿真+代码)

资料下载地址&#xff1a;基于stm32风速风向温湿度和瓦斯检测 一、项目功能 1.风速&#xff0c;风向&#xff0c;温湿度&#xff0c;瓦斯&#xff0c;报警。 2.可以设置温湿度&#xff0c;瓦斯&#xff0c;风速报警阈值。 3.数据上传到云平台。 二、仿真图 三、程序 #inc…