上一篇: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类型表单的解析和文件上传