基于正点原子阿波罗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输出:

cpp 复制代码
#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",分别粘贴以下代码:
cpp 复制代码
/*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句柄 */

}
cpp 复制代码
/*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"文件进行如下修改(注意:此处修改是博主在做项目时遇到问题做出的修改,如果你只是为了跑实验则可以不改):
cpp 复制代码
/*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_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 < 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_RAW
        if (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_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 < NUM_DEFAULT_FILENAMES; loop++) {
      const char *file_name;
#if LWIP_HTTPD_MAX_REQUEST_URI_LEN
      if (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.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 < 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_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"函数前增加以下内容:
cpp 复制代码
/************************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"函数最后增加以下内容:
cpp 复制代码
http_set_cgi_handlers(ppcURLs, NUM_CONFIG_CGI_URIS);      /* 配置CGI句柄 */
  • 注销"stm32f4xx_it.c"中关于LED反转的内容;
  • main函数"http_Init();"前添加以下内容:
cpp 复制代码
	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"的定义修改成如下内容:
cpp 复制代码
//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文件最后添加以下内容:
cpp 复制代码
/************************************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"文件进行如下修改:
cpp 复制代码
/*开头增加外部变量声明*/
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的过程,本章节实验源码、网页源文件、网页数组生成工具均在此:

相关推荐
编程小能手@1 小时前
【计算机网络】子网划分
服务器·网络·计算机网络
骷大人2 小时前
Thinkphp6实现websocket
网络·websocket·网络协议
中云时代-防御可测试-小余2 小时前
怎么选择合适的高防IP
服务器·网络·网络协议·tcp/ip·阿里云·udp·ddos
lichuangcsdn2 小时前
springboot集成websocket给前端推送消息
前端·websocket·网络协议
ladymorgana4 小时前
【OSS】 前端如何直接上传到OSS 上返回https链接,如果做到OSS图片资源加密访问
前端·网络协议·https
白棂5 小时前
面试题——计算机网络:HTTP和HTTPS的区别?
计算机网络·http·https
碳酸的唐5 小时前
现代网络安全攻防技术与发展现状
网络·安全·web安全
CodeWithMe5 小时前
【Net】TCP粘包与半包
网络·网络协议·tcp/ip
m0_555762906 小时前
使用 Let‘s Encrypt 和 Certbot 为 Cloudflare 托管的域名申请 SSL 证书
网络·网络协议·ssl
半青年6 小时前
IEC61850规约客户端软件开发实战(第二章)
java·c++·qt·网络协议·c#·信息与通信·iec61850