STM32 W5500 HTTP Server 微型web服務實現

嵌入式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 "<!DOCTYPE html>"\
"<html>"\
"<head>"\
"<meta charset=\"utf-8\">"\
"<title>SN Config</title>"\
"<style>"\
".d-c{ position:absolute;left:40%;top:49%}"\
".b-c{ position:absolute;left:40%;top:90%}"\
"</style>"\
"</head>"\
"<body>"\
"<div class=\"d-c\">"\
"<form name=\"input\" action=\"sn_config.action\" method=\"post\">"\
"SN:<input type=\"text\" name=\"sn\">"\
"<input type=\"submit\" value=\"submit\">"\
"</form>"\
"</div>	"\
"<div class=\"b-c\">"\
"<p>System SN Config</p>"\
"</div>"\
"</body>"\
"</html>\r\n"

#define HTML_RESULT_OK "<!DOCTYPE html>"\
"<html>"\
"<head>"\
"<meta charset=\"utf-8\">"\
"<title>SN Config</title>"\
"<style>"\
".c-c{ position:absolute;left:40%;top:49%}"\
"</style>"\
"</head>"\
"<body>"\
"<div class=\"c-c\">"\
"<p>System SN Config OK</p>"\
"</div>"\
"</body>"\
"</html>\r\n"

#define HTML_RESULT_ERR "<!DOCTYPE html>"\
"<html>"\
"<head>"\
"<meta charset=\"utf-8\">"\
"<title>SN Config</title>"\
"<style>"\
".c-c{ position:absolute;left:40%;top:49%}"\
"</style>"\
"</head>"\
"<body>"\
"<div class=\"c-c\">"\
"<p>System SN Config FAIL</p>"\
"</div>"\
"</body>"\
"</html>\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中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章