基于正点原子阿波罗F429开发板的LWIP应用(4)——HTTP Server功能_单片机lwip,web server的网页ui需要单独写程序吗?
说在开头
正点原子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 文档的注释内容 之中即可。如:就是一条 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 \"\" 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 voidget_tag_insert(struct http_state *hs){#if LWIP_HTTPD_SSI_RAW const 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_MULTIPART u16_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_MULTIPART current_tag_part = ssi->tag_part; ssi->tag_part = HTTPD_LAST_TAG_PART;#endif /* LWIP_HTTPD_SSI_MULTIPART */#if LWIP_HTTPD_SSI_RAW tag = 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 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_RAW if (ssi->tag_insert_len != HTTPD_SSI_TAG_UNKNOWN)#endif /* LWIP_HTTPD_SSI_RAW */ { return; } } } }/*以下代码是在识别到的未知的SSI标签的位置插入“***UNKNOWN TAG ***”,屏蔽后将不替换*/// /* 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 \"***UNKNOWN TAG \"//#define UNKNOWN_TAG1_LEN 18//#define UNKNOWN_TAG2_TEXT \"***\"//#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 tag_insert_len = (u16_t)len;}#endif /* LWIP_HTTPD_SSI *//*http_find_file函数修改成如下内容*/static err_thttp_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_CGI int i;#endif /* LWIP_HTTPD_CGI */#if !LWIP_HTTPD_SSI const#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_LEN size_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 one that exists. */ for (loop = 0; loop 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.html size_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_SSI tag_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_CGI http_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 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_SSI if (file != NULL) { if (file->flags & FS_FILE_FLAGS_SSI) { tag_check = 1; } else {#if LWIP_HTTPD_SSI_BY_FILE_EXTENSION tag_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的过程,本章节实验源码、网页源文件、网页数组生成工具均在此: