笔者通过w5500实现web服务器点灯
1.http协议概述
HTTP是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式。
2.http服务器实验设计
3.程序设计
3.1 w5500初始化
分别对w5500进行ip地址的设定、和我们单片机进行spi通信的配置。我们在w5500_conf.c文件中配置好ip地址、子网掩码、网关。要保证ip地址和网关在同一个网段内才能正常通信。再者配置好我们的spi引脚与w5500模块的连接即可完成通信。而
3.2 http server回环测试函数(附解析)
void do_https(void)
{
uint8 ch=SOCK_HTTPS; /*定义一个socket*/
uint16 len;
st_http_request *http_request; /*定义一个结构指针*/
memset(rx_buf,0x00,MAX_URI_SIZE);
http_request = (st_http_request*)rx_buf;
/* http service start */
switch(getSn_SR(ch)) /*获取socket状态*/
{
case SOCK_INIT: /*socket处于初始化状态*/
listen(ch);
break;
case SOCK_LISTEN: /*socket处于监听状态*/
break;
case SOCK_ESTABLISHED: /*socket处于连接状态*/
if(getSn_IR(ch) & Sn_IR_CON)
{
setSn_IR(ch, Sn_IR_CON); /*清除中断标志位*/
}
if ((len = getSn_RX_RSR(ch)) > 0)
{
len = recv(ch, (uint8*)http_request, len); /*接收http请求*/
*(((uint8*)http_request)+len) = 0;
proc_http(ch, (uint8*)http_request); /*接收http请求并发送http响应*/
disconnect(ch);
}
break;
case SOCK_CLOSE_WAIT: /*socket处于等待关闭状态*/
if ((len = getSn_RX_RSR(ch)) > 0)
{
len = recv(ch, (uint8*)http_request, len); /*接收http请求*/
*(((uint8*)http_request)+len) = 0;
proc_http(ch, (uint8*)http_request); /*接收http请求并发送http响应*/
}
disconnect(ch);
break;
case SOCK_CLOSED: /*socket处于关闭状态*/
socket(ch, Sn_MR_TCP, 80, 0x00); /*打开socket*/
break;
default:
break;
}
}
程序解读1:在这里声明一下,调用 do_http() 函数实现 HTTP 服务器,此函数只执行一次,除了reboot_flag==1会重启再重新执行此函数。
/**
*@brief 接收http请求报文并发送http响应,以下这段代码很重要,是http响应阶段
*@param s: http服务器socket
*@param buf:解析报文内容
*@return 无
*/
void proc_http(SOCKET s, uint8 * buf)
{
int8* name;
int8 req_name[32]={0x00,}; /*定义一个http响应报文的指针*/
unsigned long file_len=0; /*定义http请求报文头的结构体指针*/
uint16 send_len=0;
uint8* http_response;
st_http_request *http_request;
memset(tx_buf,0x00,MAX_URI_SIZE);
http_response = (uint8*)rx_buf;
http_request = (st_http_request*)tx_buf;
parse_http_request(http_request, buf); /*解析http请求报文头*/
switch (http_request->METHOD)
{
case METHOD_ERR : /*请求报文头错误*/
memcpy(http_response, ERROR_REQUEST_PAGE, sizeof(ERROR_REQUEST_PAGE));
send(s, (uint8 *)http_response, strlen((int8 const*)http_response));
break;
case METHOD_HEAD: /*HEAD请求方式*/
case METHOD_GET: /*GET请求方式*/
name = http_request->URI;
if(strcmp(name,"/index.htm")==0 || strcmp(name,"/")==0 || (strcmp(name,"/index.html")==0))
{
file_len = strlen(INDEX_HTML);
make_http_response_head((uint8*)http_response, PTYPE_HTML,file_len);
send(s,http_response,strlen((char const*)http_response));
send_len=0;
while(file_len)
{
if(file_len>1024)
{
if(getSn_SR(s)!=SOCK_ESTABLISHED)
{
return;
}
send(s, (uint8 *)INDEX_HTML+send_len, 1024);
send_len+=1024;
file_len-=1024;
}
else
{
send(s, (uint8 *)INDEX_HTML+send_len, file_len);
send_len+=file_len;
file_len-=file_len;
}
}
}
else if(strcmp(name,"/w5500.js")==0)
{
memset(tx_buf,0,MAX_URI_SIZE);
make_basic_config_setting_json_callback(tx_buf,CONTROLH);
sprintf((char *)http_response,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length:%d\r\n\r\n%s",strlen(tx_buf),tx_buf);
send(s, (u_char *)http_response, strlen((char const*)http_response));
}
break;
case METHOD_POST: /*POST请求*/
mid(http_request->URI, "/", " ", req_name); /*获取该请求的文件名*/
if(strcmp(req_name,"config.cgi")==0)
{
cgi_ipconfig(http_request); /*将配置信息写进单片机eeprom*/
make_cgi_response(5,(int8*)ConfigMsg.lip,tx_buf); /*生成响应的文本部分*/
sprintf((char *)http_response,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length:%d\r\n\r\n%s",strlen(tx_buf),tx_buf);
/*发送http响应*/
send(s, (u_char *)http_response, strlen((char *)http_response));
disconnect(s); /*断开socket连接*/
reboot_flag=1; /*重启标志位置1*/
return;
}
break;
default :
break;
}
}
/**
*@brief 功能函数,可以配置读取到的值以驱动单片机
*@param http_request:定义一个http请求的结构体指针
*@return 无
*/
void cgi_ipconfig(st_http_request *http_request)
{
uint8 * param;
param = get_http_param_value(http_request->URI,"room"); /*房间号,其实笔者是点亮LED1,读者可以对代码进行优化*/
if(param)
{
LED1_ON;
}
param = get_http_param_value(http_request->URI,"leftroom"); /*获取需要关灯的房号,笔者没配置,读者可优化*/
if(param)
{
}
param = get_http_param_value(http_request->URI,"a"); /*获取修改后的a,这里我没配置*/
if(param)
{
}
}
/*以下是笔者设计的网页*/
#ifndef __WEBPAGE_H
#define __WEBPAGE_H
#define INDEX_HTML "<!DOCTYPE html>"\
"<html>"\
"<head>"\
"<title>室内各电器配置—小兵科技</title>"\
"<meta http-equiv='Content-Type' content='text/html; charset=GB2312'/>"\
"<style type='text/css'>"\
"body {text-align:left; background-color:#c0deed;font-family:Verdana;}"\
"#main {margin-right:auto;margin-left:auto;margin-top:30px;}"\
"label{display:inline-block;width:150px;}"\
"#main h3{color:#66b3ff; text-decoration:underline;}"\
"</style>"\
"<script>"\
"function $(id) { return document.getElementById(id); };"\
"function settingsCallback(o) {"\
"if ($('txtPub')) $('txtPub').value = 100;"\
"if ($('txtRoom')) $('txtRoom').value = o.room;"\
"if ($('txtLeftroom')) $('txtLeftroom').value = o.ip;"\
"if ($('txtA')) $('txt').value = o.sub;"\
"if ($('txtB')) $('txtB').value = o.gw;"\
"};"\
"</script>"\
"</head>"\
"<body>"\
"<div id='main'>"\
"<div style='background:snow; display:block;padding:10px 20px;'>"\
"<h3>小彬科技智能家居</h3>"\
"<form id='frmSetting' method='POST' action='config.cgi'>"\
"<p><label for='txtPub'>版本号:</label><input type='text' id='txtPub' name='pub' size='16' disabled='disabled' /></p>"\
"<p><label for='txtRoom'>点亮的灯(输入1到7房间号):</label><input type='text' id='txtRoom' name='room' size='16' /></p>"\
"<p><label for='txtLeftroom'>关掉哪个灯(输入1到7房间号):</label><input type='text' id='txtLeftroom' name='leftroom' size='16' /></p>"\
"<p><label for='txtA'>未开发功能:</label><input type='text' id='txtA' name='a' size='16' /></p>"\
"<p><label for='txtB'>未开放功能:</label><input type='text' id='txtB' name='b' size='16' /></p>"\
"<p><input type='submit' value='确认并执行' /></p>"\
"</form>"\
"</div>"\
"</div>"\
"<div style='margin:5px 5px;'>"\
"©Copyright 2021 by 清风芝明"\
"</div>"\
"<script type='text/javascript' src='w5500.js'></script>"\
"</body>"\
"</html>"
#endif
3.3主函数
#include <stdio.h>
#include <string.h>
#include "stm32f10x.h"
#include "bsp_usart1.h"
#include "bsp_i2c_ee.h"
#include "bsp_i2c_gpio.h"
#include "bsp_led.h"
#include "w5500.h"
#include "W5500_conf.h"
#include "socket.h"
#include "utility.h"
/*app函数头文件*/
#include "http_server.h"
#include "httputil.h"
uint8 reboot_flag = 0;
int main(void)
{
systick_init(72); /*初始化Systick工作时钟*/
USART1_Config(); /*初始化串口通信:115200@8-n-1*/
i2c_CfgGpio(); /*初始化eeprom*/
LED_GPIO_Config(); //笔者点灯,所以初始化,读者想增加更多功能也可增加初始化...
printf(" 网络初始化 Demo V1.0 \r\n");
gpio_for_w5500_config(); /*初始化MCU相关引脚*/
reset_w5500(); /*硬复位W5500*/
set_w5500_mac(); /*配置MAC地址*/
set_w5500_ip(); /*配置IP地址*/
socket_buf_init(txsize, rxsize); /*初始化8个Socket的发送接收缓存大小*/
printf(" 应用程序执行中…… \r\n");
printf(" 在IE浏览器中输入 W5500的IP就可访问web Server \r\n");
while(1) /*循环执行的函数*/
{
do_https();/*Web server测试程序*/
// if(reboot_flag==1)
// reboot();
}
}
4.成果演示
在点亮的灯处输入1
点击确认并执行之后的现象
笔者的代码应该还有更多值得优化的地方,这应该也算是一个应用,不过存在一些缺陷,点完灯之后需要手动去复位开发板以熄灭灯等等,可以后续进行优化。通过编写网页也更加深刻理解http的响应机制,也是具有不错的收获,读者也可以试着自己改写一个网页。