嵌入式Web服务很常见,比如电脑通过WIFI接入网络,在浏览器地址栏输入 "192.168.1.1",或者其他地址,跟自己电脑的IP在同一个网段内,一般IP最后一段是1即可,可以打开路由器的管理页面。就像这样:
这个页面就是有嵌入式小型web服务提供的网页。
STM32 W5500几乎也可以实现上述的功能,但是由于STM32的RAM和FLASH储存大小是有限的,实现一个简单的web服务是没有问题的。
STM32 W5500实现一个简单的web服务需要具备的几个条件:
1、STM32 W5500的基础入网配置,可以PING通,可以参考《STM32F103RC驱动W5500入网,并可ping通》
2、STM32 W5500的TCP Server回环测试没有问题,可以参考 《STM32 W5500 TCP Server 回环测试》
3、对HTTP协议有一定的认识。
确定一下要实现的web服务的功能,STM32 W5500配置入网后,通过DHCP动态获取IP地址,在电脑浏览器地址栏输入这个IP地址,可以获取到一个简单form表单的页面,在表单中输入数据,提交到W5500web服务,返回一个结果。如图:
功能比较简单,比较费事儿的是字符串的处理接收到的字符串的解析,以及返回信息的组装。
直接贴出测试的代码:
#ifndef __STM32F10X_H
#define __STM32F10X_H
#include "stm32f10x.h"
#endif
#ifndef __Z_UTIL_TIME_H
#define __Z_UTIL_TIME_H
#include "z_util_time.h"
#endif
#ifndef __Z_HARDWARE_LED_H
#define __Z_HARDWARE_LED_H
#include "z_hardware_led.h"
#endif
#ifndef __Z_HARDWARE_SPI_H
#define __Z_HARDWARE_SPI_H
#include "z_hardware_spi.h"
#endif
#ifndef __W5500_H
#define __W5500_H
#include "w5500.h"
#endif
#ifndef __SOCKET_H
#define __SOCKET_H
#include "socket.h"
#endif
#ifndef __W5500_CONF_H
#define __W5500_CONF_H
#include "w5500_conf.h"
#endif
#ifndef __DHCP_H
#define __DHCP_H
#include "dhcp.h"
#endif
#ifndef __Z_HARDWARE_USART2_H
#define __Z_HARDWARE_USART2_H
#include "z_hardware_usart2.h"
#endif
#include "MQTTPacket.h"
#ifndef __IMPL_MQTT_H
#define __IMPL_MQTT_H
#include "impl_mqtt.h"
#endif
#define SOCK_TCPS 0
#define BUFFER_SIZE 1536
#define TEXT_TEMPLATE_OK "HTTP/1.1 200 OK\r\n"\
"Content-Type: text/html\r\n"\
"Content-Length: %d\r\n"\
"Connection: keep_alive\r\n\r\n%s"
#define TEXT_TEMPLATE_ERR "HTTP/1.1 404 Not Found\r\n"\
"Content-Length: 0\r\n\r\n"\
"Connection: keep_alive\r\n\r\n"
#define HTML_CONTENT ""\
""\
""\
""\
"SN Config "\
""\
""\
""\
""\
""\
" "\
""\
"System SN Config
"\
""\
""\
"\r\n"
#define HTML_RESULT_OK ""\
""\
""\
""\
"SN Config "\
""\
""\
""\
""\
"System SN Config OK
"\
""\
""\
"\r\n"
#define HTML_RESULT_ERR ""\
""\
""\
""\
"SN Config "\
""\
""\
""\
""\
"System SN Config FAIL
"\
""\
""\
"\r\n"
u8 func_analysis_http_request(u8* buffer, u16 len_recv, char* method, char* uri, char* data_body);
u8 func_package_http_response(u8* buffer, u16 *len_ret, u16 len_buf, char* cont, u16 len_cont);
int main(void)
{
u32 dhcp_timestamp;
u8 mac[6]={0, };
DHCP_Get dhcp_get;
u16 len;
u8 buffer[BUFFER_SIZE];
char http_method[16];
char http_uri[64];
char http_body[256];
u8 res_code;
systick_configuration();
init_led();
init_system_spi();
func_w5500_reset();
init_hardware_usart2_dma(9600);
getMacByLockCode(mac);
setSHAR(mac);
sysinit(txsize, rxsize);
setRTR(2000);
setRCR(3);
//DHCP
for(;func_dhcp_get_ip_sub_gw(1, mac, &dhcp_get, 500) != 0;);
if(func_dhcp_get_ip_sub_gw(1, mac, &dhcp_get, 500) == 0)
{
setSUBR(dhcp_get.sub);
setGAR(dhcp_get.gw);
setSIPR(dhcp_get.lip);
close(1);
}
dhcp_timestamp = get_systick_timestamp();
for(;;)
{
if(get_systick_timestamp() - dhcp_timestamp > 59*1000)// 1 min dhcp
{
dhcp_timestamp = get_systick_timestamp();
if(func_dhcp_get_ip_sub_gw(1, mac, &dhcp_get, 500) == 0)
{
setSUBR(dhcp_get.sub);
setGAR(dhcp_get.gw);
setSIPR(dhcp_get.lip);
close(1);
}
}
switch(getSn_SR(SOCK_TCPS))
{
case SOCK_CLOSED:
socket(SOCK_TCPS, Sn_MR_TCP, 80, Sn_MR_ND);
break;
case SOCK_INIT:
listen(SOCK_TCPS);
break;
case SOCK_ESTABLISHED:
if(getSn_IR(SOCK_TCPS) & Sn_IR_CON)
{
setSn_IR(SOCK_TCPS, Sn_IR_CON);
}
len = getSn_RX_RSR(SOCK_TCPS);
if(len>0)
{
memset(buffer, 0, BUFFER_SIZE);
len = recv(SOCK_TCPS, buffer, len);
//analysis tcp msg, and package the feedback msg
if(len > 0)
{
res_code = func_analysis_http_request(buffer, len, http_method, http_uri, http_body);
memset(buffer, 0, sizeof(buffer));
if(res_code == 0)
{
if(strcmp("GET", http_method) == 0 && strcmp("/", http_uri) == 0)
{
func_package_http_response(buffer, &len, sizeof(buffer), HTML_CONTENT, strlen(HTML_CONTENT));
send(SOCK_TCPS, buffer, len);
}
else if(strcmp("POST", http_method) == 0 && strcmp("/sn_config.action", http_uri) == 0)
{
func_package_http_response(buffer, &len, BUFFER_SIZE, HTML_RESULT_OK, strlen(HTML_RESULT_OK));
send(SOCK_TCPS, buffer, len);
}
else
{
memcpy(buffer, TEXT_TEMPLATE_ERR, strlen(TEXT_TEMPLATE_ERR));
send(SOCK_TCPS, buffer, strlen(TEXT_TEMPLATE_ERR));
}
disconnect(SOCK_TCPS);
}
else
{
memcpy(buffer, TEXT_TEMPLATE_ERR, strlen(TEXT_TEMPLATE_ERR));
send(SOCK_TCPS, buffer, strlen(TEXT_TEMPLATE_ERR));
disconnect(SOCK_TCPS);
}
}
}
break;
case SOCK_CLOSE_WAIT:
close(SOCK_TCPS);
break;
}
func_led1_on();
delay_ms(500);
func_led1_off();
delay_ms(500);
}
}
u8 func_analysis_http_request(u8* buffer, u16 len_recv, char* method, char* uri, char* data_body)
{
char chs[BUFFER_SIZE] = {0, };
char *res, *end;
if(len_recv > 0)
{
memcpy(chs, buffer, 3);
res = strstr(chs, "GET");
if(strcmp("GET", res) == 0)
{
memcpy(method, "GET", strlen("GET"));
}
else
{
memset(chs, 0, BUFFER_SIZE);
memcpy(chs, buffer, 4);
res = strstr(chs, "POST");
if(strcmp("POST", res) == 0)
{
memcpy(method, "POST", strlen("POST"));
}
else
{
return 1;
}
}
memset(chs, 0, BUFFER_SIZE);
memcpy(chs, buffer, len_recv + 1);
res = strchr(chs, '/');
if(res != NULL)
{
end = strchr(res, ' ');
if(end != NULL)
{
memcpy(uri, res, end - res);
}
}
memset(chs, 0, BUFFER_SIZE);
memcpy(chs, buffer, len_recv + 1);
res = strstr(chs, "\r\n\r\n");
if(res != NULL)
{
if(strlen(res) > 4)
{
memcpy(data_body, res + 4, strlen(res) - 4);
}
}
}
return 0;
}
u8 func_package_http_response(u8* buffer, u16 *len_ret, u16 len_buf, char* cont, u16 len_cont)
{
memset(buffer, 0, BUFFER_SIZE);
*len_ret = sprintf((char*)buffer, TEXT_TEMPLATE_OK, len_cont, cont);
return 0;
}
相关的基础函数库可以参考我的其他文章。
测试步骤,我的W5500的IP是动态获取到的,所以先登录到路由器管理页面查看他的IP地址。
我的 STM32 W5500 IP地址是"192.168.1.100"。在浏览器地址栏输入"192.168.1.100",可以看到页面
在SN后的input标签内输入任意内容,点击submit后,可以看到
数据已提交到服务。
通过Debug单步调试可以看到浏览器提交到的数据内容,或者通过串口打印出来。一般的操作都是收到这个数据后存储到EEPROM中。