基于正點原子阿波羅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);//′ò?aμ???ò3//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
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/82656.shtml
英文地址,請注明出處:http://en.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…