W5500EVB as HTTP Server

Nowadays, it is common to use hardware as an HTTP server. The web network configuration of a router is a typical example. W5500 allows us to use it as an HTTP server to achieve various goals. For instance, it can work as a network configuration web page for itself, like a router does. Working as a server, W5500EVB store HTML data in its database(memory), and return the requested data to client, displaying the HTML as a website through web browser.

Before burning the bin file into your EVB:

      1. Connect W5500 EVB to the router/NAT that you are using, or any other device that allows you to connect to the internet
      2. Manually change the Network configuration: 
        • Go to “w5500_conf.c”, make sure the subnet mask, local IP address, gateway and DNS are set according to the internet setting you are using with your router/NAT
      3. Rebuild the sample code and burn it into W5500EVB


Code Explanation

After boot or reboot W5500EVB, all necessary configurations are initialized.

uint8 reboot_flag = 0;
int main(void)
{	
  systick_init(72);
  USART1_Config();
  i2c_CfgGpio();	

  printf(" W5500EVB Init Complete!\r\n");

  gpio_for_w5500_config();
  reset_w5500();
  set_w5500_mac();
  set_w5500_ip();
  socket_buf_init(txsize, rxsize);

  printf("Please type the IP address to search engine to enter its webpage\r\n"); 
  while(1)
  {
    do_https();
    do_netbios();
  }
}
/******************




These are all the necessary
initialization





The IP address would be shown 
in Serial




****************/



After the necessary initialization of W5500EVB and network configuration, the while loop starts looping two functions:

do_netbios()

This is a function for the domain name of the webpage, and it is not necessary and applicable in this sample code.

do_https()

The function is mainly divided into five socket states, which would be elaborated below.


Socket Initialized

After initialization, W5500EVB goes into the main while loop, and open a socket:

void do_https(void)
{
  uint8 ch=SOCK_HTTPS;
  uint16 len;

  st_http_request *http_request;
  memset(rx_buf,0x00,MAX_URI_SIZE);
  http_request = (st_http_request*)rx_buf;

  switch(getSn_SR(ch))
  {
/***************




Necessary Variables declared 




****************/


Socket Established

Once connection established with the client, this state activates. During this state, W5500EVB first checks if there is any packet from client by checking whether there is an interrupt. Once interrupt is discovered, it gets the packet from its register and parses it to identify what kind of request it is. Then, it responses to the identified request through function “proc_http()” (this function will be discussed below). Finally the connection ends and W5500EVB closes the socket for the next connection.

case SOCK_ESTABLISHED:

  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);
    *(((uint8*)http_request)+len)= 0;

    proc_http(ch,(uint8*)http_request);

    disconnect(ch);
  }

  break;
/*************


Make sure w5500EVB is in Connection state



Check if there is any packet from client 
by checking whether there is an interrupt.

Get the packet from its register and parses 
it to identify what kind of request it is. 

Responses to the identified request 
through function “proc_http()”



****************/


Socket Close Wait

W5500 turns into this state when it receives a FIN packet from the connected socket. This scenario would not happen in this sample code.

case SOCK_CLOSE_WAIT:
  if ((len = getSn_RX_RSR(ch)) > 0)
  {
    len = recv(ch, (uint8*)http_request, len); 
    *(((uint8*)http_request)+len) = 0;
    proc_http(ch, (uint8*)http_request);
  }

  disconnect(ch);
  break;


Socket Closed

It indicates that the socket is closed. In the sample code, once W5500EVB enters this state, it opens a socket immediately after entering this state for next http request from client.

  case SOCK_CLOSED:
    socket(ch, Sn_MR_TCP, 80, 0x00);
    break;

  default:
    break;
  }
}
/***************


Socket was closed and open again for
next connection


****************/


proc_http()

As mentioned above in the Socket established state, this is a crucial function in this sample code. It manages the parse of requests from client, does changes according to requests, and sends relevant responses back to the client.

void proc_http(SOCKET s, uint8 * buf)
{
  int8* name;						
  int8 req_name[32]={0x00,};
  unsigned long file_len=0;
  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);

  switch (http_request->METHOD)		
  {
/***************




Necessary Variable declared 




****************/

First, it parses the request and identify the method of it. By using switch statement, it chooses data to reply according to the method, which would probably be ERROR, HEAD, GET, and POST. In the sample code, GET would probably be the only method we will receive.


METHOD_GET

Since the request is parsed, we know also the Uniform Resource Identifier (URI) of the response. In our sample code, there are only three sets of html codes for response, with the same domain name. One of the html code would be sent back to client according to the URI.

case METHOD_GET:
name = http_request->URI;


The first one of three sets of html codes is the front page, with “/index.html” at the end of the address

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;
    } 
  }
}


The second one is a page showing a list of sample code(only the list), with “/dream.htm” at the end of the address.

else if(strcmp(name,"/dream.htm")==0 || strcmp(name,"/")==0 || (strcmp(name,"/dream.html")==0))
{
  file_len = strlen(DREAM_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 *)DREAM_HTML+send_len, 1024);
  send_len+=1024;
  file_len-=1024;
  }
  else
  {
    send(s, (uint8 *)DREAM_HTML+send_len, file_len);
    send_len+=file_len;
    file_len-=file_len;
    } 
  }
}


The third one is a page for network configuration of the W5500EVB, with “/config.htm” at the end of the address.

else if(strcmp(name,"/config.htm")==0 || strcmp(name,"/")==0 || (strcmp(name,"/config.html")==0))
{
  file_len = strlen(CONFIG_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 *)CONFIG_HTML+send_len, 1024);
    send_len+=1024;
    file_len-=1024;
    }
    else
    {
    send(s, (uint8 *)CONFIG_HTML+send_len, file_len);
    send_len+=file_len;
    file_len-=file_len;
    } 
  }
}


This is for the response head, which would be discussed later in the page.

else if(strcmp(name,"/w5500.js")==0)
{
  memset(tx_buf,0,MAX_URI_SIZE);
  make_basic_config_setting_json_callback(tx_buf,ConfigMsg);
  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;


METHOD_POST

According to HTTP protocol, there would be two packets to send back to client as response: first response head and then html code. The response head would be generated by make_http_response_head() based on the type and the length of the html code for response; the html codes are already stored in W5500EVB as variables “INDEX_HTML”, “DREAM_HTML” and  “CONFIG_HTML”.

case METHOD_POST:
  mid(http_request->URI, "/", " ", req_name);
  if(strcmp(req_name,"config.cgi")==0)							  	
  {
    cgi_ipconfig(http_request);
    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);

    send(s, (u_char *)http_response, strlen((char *)http_response));		
    disconnect(s);
    reboot_flag=1;
    return;
  }
  break;


METHOD_ERR & METHOD_HEAD

As mentioned, these two cases would not happen in general. They are for further development, or in case there were errors.

  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:


  default :
    break;
  }
}



Remarks

If you try to examine the sample code, you may discover that there will be two GET requests from the client every time you type the IP address in the search engine. The first one is the request for html code, which is the webpage you request; the second one is a request for “favicon.ico”, which is the icon next to the title of the tab on the top of your browser. Since our sample code does not provide one, there will be no response to that.