上一篇:lwip-2.1.3自带的httpd网页服务器使用教程(二)使用SSI动态生成网页部分内容
认识URL参数
在上网的时候,我们经常会见到在网址后面带有?A=B&C=D这样的语法格式。例如:
https://blog.csdn.net/ZLK1214/article/details/129151458?csdn_share_tail={%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22129151458%22%2C%22source%22%3A%22ZLK1214%22}
 上面这个网址就带有csdn_share_tail参数,等号后面是参数的值。如果有多个参数的话,中间可用&符号连接。
另外我们也可以把网页中的表单的method属性设为get,表单提交后表单的内容也是以URL参数的方式呈现的。
<form name="form1" method="get">
  <label>
    器件名称:
    <input name="devname" type="text" id="devname" value="STM32">
  </label><br>
  <label>
    器件类型: 
    <select name="devtype" id="devtype">
      <option value="1">单片机芯片</option>
      <option value="2">网络芯片</option>
      <option value="3">音频芯片</option>
    </select>
  </label><br>
  <input type="submit" value="搜索">
</form> 
<input type="submit" value="搜索">是表单提交按钮,点击按钮后凡是带有name属性的控件的名称和值都会出现在URL中。
 例如,上面的表单提交后,浏览器跳转的网址就是:http://stm32f103ze/info.ssi?devname=STM32&devtype=1。
lwip httpd服务器提供的CGI功能就是用来获取这样的URL参数的。lwip提供的CGI功能分为两种:旧式CGI和新式CGI。
使用旧式CGI功能
旧式CGI的功能比较简单,用http_set_cgi_handlers函数指定一些支持URL参数的网页,经过指定的回调函数处理后,跳转到另一页面上(也可以选择不跳转)。
 http_set_cgi_handlers的原型如下。
 typedef struct
 {
     const char *pcCGIName;
     tCGIHandler pfnCGIHandler;
 } tCGI;
 void http_set_cgi_handlers(const tCGI *cgis, int num_handlers);
 其中,参数cgis为tCGI结构体数组(必须是全局变量,不能是局部变量),参数num_handlers为tCGI结构体数组的大小,可由LWIP_ARRAYSIZE宏计算数组的大小。
 tCGI结构体里面的pcCGIName是网页的名称,pfnCGIHandler是处理该网页的URL参数的回调函数。回调函数的原型如下:
 const char *XXX(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]);
 参数iIndex是当前正在处理的网页在tCGI结构体数组中的下标,那么当前正在处理的网页名称就是“全局数组名[iIndex].pcCGIName”。
 iNumParams是URL参数的个数,是pcParam数组和pcValue数组的元素个数。
 pcParam数组和pcValue数组分别是参数名列表和对应的参数值的列表。
 回调函数的返回值是要跳转的网页名称(浏览器的地址栏上显示的仍然还是跳转前的网页文件名),如果不想跳转到另一网页,可直接返回当前网页名称“全局数组名[iIndex].pcCGIName”。
 tCGI结构体的pcCGIName成员(跳转前的页面名称),和pfnCGIHandler回调函数的返回值(要跳转的页面)都可以是虚拟页面,并不是必须要在文件系统上能找得到。
旧式CGI的不足之处是URL参数无法和当前HTTP连接的SSI功能(标签替换功能)直接交互。
示例:使用URL参数控制LED灯的亮灭和数码管的显示
(本节例程名称:cgi_test)
 首先,在lwip-2.1.3/apps/http/fs下放入动态网页devctrl.ssi,然后运行makefsdata程序打包。devctrl.ssi的内容如下:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>设备控制页</title>
<style type="text/css">
<!--
body {
	font-family: "Times New Roman", Times, serif;
	background: #666666;
	margin: 0;
	padding: 0;
	text-align: center;
	color: #000000;
}
#container {
	width: 780px;
	background: #FFFFFF;
	margin: 0 auto;
	border: 1px solid #000000;
	text-align: left;
}
#header {
	background: #DDDDDD; 
	padding: 0 10px 0 20px;
}
#header h1 {
	margin: 0;
	padding: 10px 0;
}
#mainContent {
	padding: 30px 20px;
	background: #FFFFFF;
	font-size: 14px;
}
#footer {
	padding: 0 10px;
	background: #DDDDDD;
}
#footer p {
	margin: 0;
	padding: 10px 0;
}
-->
</style>
</head>
<body>
<div id="container">
  <div id="header">
    <h1>设备控制页</h1>
  </div>
  <div id="mainContent">
    <form name="form1" method="get" action="">
      <table width="100%" border="0">
        <tr>
          <td width="30%" align="right">LED1: </td>
          <td align="left">
            <label>
              <input type="radio" name="led1" value="on"<!--#led1_on-->>
              亮
            </label>
            <label>
              <input type="radio" name="led1" value="off"<!--#led1_off-->>
              灭
            </label>
          </td>
        </tr>
        <tr>
          <td align="right">LED2: </td>
          <td align="left">
            <label>
              <input type="radio" name="led2" value="on"<!--#led2_on-->>
              亮
            </label>
            <label>
              <input type="radio" name="led2" value="off"<!--#led2_off-->>
              灭
            </label>
          </td>
        </tr>
        <tr>
          <td align="right">LED3: </td>
          <td align="left">
            <label>
              <input type="radio" name="led3" value="on"<!--#led3_on-->>
              亮
            </label>
            <label>
              <input type="radio" name="led3" value="off"<!--#led3_off-->>
              灭
            </label>
          </td>
        </tr>
        <tr>
          <td align="right"><label for="num">数码管: </label></td>
          <td align="left"><input type="text" name="num" id="num" style="width: 100px" value="<!--#segnum-->"></td>
        </tr>
        <tr>
          <td align="right"></td>
          <td align="left"><input type="submit" value="确定"></td>
        </tr>
      </table>
    </form>
  </div>
  <div id="footer">
    <p><b>当前时间: </b><!--#datetime--></p>
  </div>
</div>
</body>
</html>
 
修改lwipopts.h里面的HTTPD选项,开启CGI功能和SSI功能:
// 配置HTTPD
#define LWIP_HTTPD_CGI 1
#define LWIP_HTTPD_SSI 1
#define LWIP_HTTPD_SSI_INCLUDE_TAG 0
#define LWIP_HTTPD_SSI_RAW 1 
编写test.c文件,其中test_init函数在main函数中调用了httpd_init()之后调用。
#include <lwip/apps/httpd.h>
#include <lwip/def.h>
#include <stm32f1xx.h>
#include <string.h>
#include <time.h>
#include "SegDisplay.h"
#include "test.h"
static float test_num;
static tCGI test_cgis[3];
static const char *test_cgis_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[])
{
  char *endptr;
  float num;
  int i;
  
  for (i = 0; i < iNumParams; i++)
  {
    if (strcasecmp(pcParam[i], "led1") == 0)
    {
      if (strcasecmp(pcValue[i], "on") == 0)
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_SET);
      else if (strcasecmp(pcValue[i], "off") == 0)
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_RESET);
      else if (strcasecmp(pcValue[i], "toggle") == 0)
        HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_5);
    }
    else if (strcasecmp(pcParam[i], "led2") == 0)
    {
      if (strcasecmp(pcValue[i], "on") == 0)
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
      else if (strcasecmp(pcValue[i], "off") == 0)
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
      else if (strcasecmp(pcValue[i], "toggle") == 0)
        HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
    }
    else if (strcasecmp(pcParam[i], "led3") == 0)
    {
      if (strcasecmp(pcValue[i], "on") == 0)
        HAL_GPIO_WritePin(GPIOE, GPIO_PIN_6, GPIO_PIN_SET);
      else if (strcasecmp(pcValue[i], "off") == 0)
        HAL_GPIO_WritePin(GPIOE, GPIO_PIN_6, GPIO_PIN_RESET);
      else if (strcasecmp(pcValue[i], "toggle") == 0)
        HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_6);
    }
    else if (strcasecmp(pcParam[i], "num") == 0)
    {
      num = strtof(pcValue[i], &endptr);
      if (*endptr == '\0')
      {
        test_num = num;
        SegDisplay_SetFloatNumber(num);
      }
    }
  }
  return "/devctrl.ssi";
}
static u16_t test_ssi_handler(const char *ssi_tag_name, char *pcInsert, int iInsertLen)
{
  struct tm tm;
  time_t t;
  
  if (strcmp(ssi_tag_name, "led1_on") == 0)
  {
    if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_5) == GPIO_PIN_SET)
      snprintf(pcInsert, iInsertLen, " checked");
    else
      snprintf(pcInsert, iInsertLen, "");
  }
  else if (strcmp(ssi_tag_name, "led1_off") == 0)
  {
    if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_5) == GPIO_PIN_RESET)
      snprintf(pcInsert, iInsertLen, " checked");
    else
      snprintf(pcInsert, iInsertLen, "");
  }
  else if (strcmp(ssi_tag_name, "led2_on") == 0)
  {
    if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_SET)
      snprintf(pcInsert, iInsertLen, " checked");
    else
      snprintf(pcInsert, iInsertLen, "");
  }
  else if (strcmp(ssi_tag_name, "led2_off") == 0)
  {
    if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_RESET)
      snprintf(pcInsert, iInsertLen, " checked");
    else
      snprintf(pcInsert, iInsertLen, "");
  }
  else if (strcmp(ssi_tag_name, "led3_on") == 0)
  {
    if (HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_6) == GPIO_PIN_SET)
      snprintf(pcInsert, iInsertLen, " checked");
    else
      snprintf(pcInsert, iInsertLen, "");
  }
  else if (strcmp(ssi_tag_name, "led3_off") == 0)
  {
    if (HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_6) == GPIO_PIN_RESET)
      snprintf(pcInsert, iInsertLen, " checked");
    else
      snprintf(pcInsert, iInsertLen, "");
  }
  else if (strcmp(ssi_tag_name, "segnum") == 0)
    snprintf(pcInsert, iInsertLen, "%g", test_num);
  else if (strcmp(ssi_tag_name, "datetime") == 0)
  {
    time(&t);
    localtime_r(&t, &tm);
    strftime(pcInsert, iInsertLen, "%Y-%m-%d %H:%M:%S", &tm);
  }
  else
    return HTTPD_SSI_TAG_UNKNOWN;
  return strlen(pcInsert);
}
static void test_led_init(void)
{
  GPIO_InitTypeDef gpio;
  
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOE_CLK_ENABLE();
  
  gpio.Mode = GPIO_MODE_OUTPUT_PP;
  gpio.Pin = GPIO_PIN_5 | GPIO_PIN_13;
  gpio.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &gpio);
  
  gpio.Pin = GPIO_PIN_6;
  HAL_GPIO_Init(GPIOE, &gpio);
}
void test_init(void)
{
  test_led_init();
  SegDisplay_Init();
  
  test_cgis[0].pcCGIName = "/devctrl";
  test_cgis[0].pfnCGIHandler = test_cgis_handler;
  test_cgis[1].pcCGIName = "/devctrl.ssi";
  test_cgis[1].pfnCGIHandler = test_cgis_handler;
  test_cgis[2].pcCGIName = "/devctrl.html";
  test_cgis[2].pfnCGIHandler = test_cgis_handler;
  http_set_cgi_handlers(test_cgis, LWIP_ARRAYSIZE(test_cgis));
  http_set_ssi_handler(test_ssi_handler, NULL, 0);
}
 
程序运行结果:
 访问网址:http://stm32f103ze/devctrl?led1=toggle&led2=toggle&led3=toggle&num=-13.9
 可以看到数码管显示了-13.9这个数字。每访问一次网页,三个LED灯都会切换一次状态。
 还可以在网页中通过表单控件动态改变URL参数的值,表单上也会显示当前LED和数码管的状态。


使用新式CGI功能
下一篇:lwip-2.1.3自带的httpd网页服务器使用教程(四)POST类型表单的解析和文件上传



















