Wiznet makers

teddy

Published August 02, 2023 ©

97 UCC

8 WCC

73 VAR

0 Contests

0 Followers

0 Following

Original Link

STM Lesson 91. LAN. W5500. HTTP Server. Part 3

STM Lesson 91. LAN. W5500. HTTP Server. Part 3

COMPONENTS
PROJECT DESCRIPTION

Part 3

 

LAN. W5500. HTTP server
 

 

 
 

 

B previous part lesson we wrote the function of initializing the chip, and also began to write the function of receiving and processing network packages.

 

Since we are writing an HTTP server, let's put all the work with this protocol into a separate module for which we will create files httpd.h and httpd.c as follows

 

httpd.h

#ifndef HTTPD_H_

#define HTTPD_H_

//--------------------------------------------------

#include "stm32f4xx_hal.h"

#include <string.h>

#include <stdlib.h>

#include <stdint.h>

#include "fatfs.h"

#include "w5500.h"

//--------------------------------------------------

#endif /* HTTPD_H_ */

 

httpd.c

#include "httpd.h"

//-----------------------------------------------

extern UART_HandleTypeDef huart2;

//-----------------------------------------------

extern char str1[60];

//-----------------------------------------------

 

Also connect this library in the file w5500.h in bottom itself file

 

void w5500_packetReceive(void);

//--------------------------------------------------

#include "httpd.h"

//--------------------------------------------------

 

In file httpd.h add a structure for the properties of the HTTP package, as well as macros for document types

 

#include "w5500.h"

//--------------------------------------------------

typedef struct http_sock_prop {

volatile uint8_t data_stat;//статус передачи данных

volatile uint32_t data_size;//размер данных для передачи

volatile uint16_t last_data_part_size;//размер последней части данных для передачи

volatile uint16_t cnt_data_part;//общее количество частей данных для передачи

volatile uint16_t cnt_rem_data_part;//количество оставшихся частей данных для передачи

volatile uint32_t total_count_bytes;//количество переданных байтов документа

volatile uint8_t http_doc;//вариант документа для передачи

volatile uint8_t prt_tp;//вариант документа для передачи

char fname[20];//имя файла (документа)

} http_sock_prop_ptr;

//--------------------------------------------------

//Варианты документов HTTP

#define EXISTING_HTML 0

#define E404_HTML 1

#define EXISTING_JPG 2

#define EXISTING_ICO 3

//--------------------------------------------------

 

In file httpd.c add a variable of our structure, as well as connect the structure of the properties of TCP

 

extern char str1[60];

//-----------------------------------------------

http_sock_prop_ptr httpsockprop[2];

extern tcp_prop_ptr tcpprop;

 

In the w5500.c file, also connect the HTTP property structure variable

 

tcp_prop_ptr tcpprop;

extern http_sock_prop_ptr httpsockprop[2];

 

In file w5500.h add macro substitutions for data transfer states

 

} tcp_prop_ptr;

//--------------------------------------------------

//Статусы передачи данных

#define DATA_COMPLETED 0 //передача данных закончена

#define DATA_ONE 1 //передаём единственный пакет

#define DATA_FIRST 2 //передаём первый пакет

#define DATA_MIDDLE 3 //передаём средний пакет

#define DATA_LAST 4 //передаём последний пакет

#define DATA_END 5 //закрываем соединение после передачи данных

//--------------------------------------------------

 

In file w5500.c in the function of receiving a package in the body of our condition of the socket, add another condition of the state of data transfer

 

if(GetSocketStatus(tcpprop.cur_sock)==SOCK_ESTABLISHED)

{

  if(httpsockprop[tcpprop.cur_sock].data_stat == DATA_COMPLETED)

  {

  }

}

 

In the body of this condition, we will accept the HTTP request, provided that the data from us is all transferred. And, since the macro of this state matters to us 0, then we do not need to initialize the state at the start of the program and we will immediately enter the body of this condition.

Above the initialization function, add a new function for determining the size of the accepted data in the TCP package, using a special socket register for this

 

//-----------------------------------------------

uint16_t GetSizeRX(uint8_t sock_num)

{

  uint16_t len;

  uint8_t opcode=0;

  opcode = (((sock_num<<2)|BSB_S0)<<3)|OM_FDM1;

  len = (w5500_readReg(opcode,Sn_RX_RSR0)<<8|w5500_readReg(opcode,Sn_RX_RSR1));

  return len;

}

//-----------------------------------------------

 

Further in the body of an already new condition, we will find out the size of the accepted data, display it in the terminal program, and if it is zero, then there is no data in the TCP package, then we exit the function

 

if(httpsockprop[tcpprop.cur_sock].data_stat == DATA_COMPLETED)

{

  //Отобразим размер принятых данных

  len = GetSizeRX(0);

  sprintf(str1,"len_rx_buf:0x%04Xrn",len);

  HAL_UART_Transmit(&huart2,(uint8_t*)str1,strlen(str1),0x1000);

  //Если пришел пустой пакет, то уходим из функции

  if(!len) return;

}

 

We collect the code, send the controller and try to request any page. Although, of course, our server will not answer in any way, but at least we will see that a package of a certain length has arrived

 

image30

In the terminal program, we see endlessly incoming data with the size of the data length, that is, until we process the data, we will fall into this condition. and this is independent, whether or not our connection will close

 

image31

 

We also see in WareShark that there was only one request from the client

 

image32

 

And to stop this "endless cycle" is only possible by restarting the controller.

But at least we see that the request has come to us.

Also above the initialization function we write a function that will return the data address in the receiving buffer, also using the corresponding register of socket

 

//-----------------------------------------------

uint16_t GetReadPointer(uint8_t sock_num)

{

  uint16_t point;

  uint8_t opcode;

  opcode = (((sock_num<<2)|BSB_S0)<<3)|OM_FDM1;

  point = (w5500_readReg(opcode,Sn_RX_RD0)<<8|w5500_readReg(opcode,Sn_RX_RD1));

  return point;

}

//-----------------------------------------------

 

Add a prototype for this function. It will come in handy for us in another module.

Let's also find out the data address in the buffer of their reception in the body of the same conditions as the reading and processing of network packages, also displaying it in the terminal program

 

if(!len) return;

//здесь обмениваемся информацией: на запрос документа от клиента отправляем ему запрошенный документ

//указатель на начало чтения приёмного буфера

point = GetReadPointer(tcpprop.cur_sock);

sprintf(str1,"Sn_RX_RD:0x%04Xrn",point);

HAL_UART_Transmit(&huart2,(uint8_t*)str1,strlen(str1),0x1000);

 

We collect the code, send the controller and once again request the document in the browser. And here is the result

 

image33

 

Our address is still zero. Well, it is understandable. This is the very first request and it has not been processed. Therefore, the address will not change until it is.

Add global array for temporary buffer

 

extern char str1[60];

char tmpbuf[30];

 

In file w5500.h add a structure for the transfer buffer

 

} tcp_prop_ptr;

//--------------------------------------------------

typedef struct data_sect {

  volatile uint16_t addr;

  volatile uint8_t opcode;

  uint8_t data[];

} data_sect_ptr;

//--------------------------------------------------

 

We will also add macros for TCP protocol options. So far there will be only two — unknown and HTTP

 

#define DATA_END 5 //закрываем соединение после передачи данных

//--------------------------------------------------

//Варианты протоколов TCP

#define PRT_TCP_UNCNOWN 0

#define PRT_TCP_HTTP 1

//--------------------------------------------------

 

Let's go back to the file w5500.c and after the register reading function, add a few more read functions

 

//-----------------------------------------------

void w5500_readBuf(data_sect_ptr *datasect, uint16_t len)

{

  SS_SELECT();

  HAL_SPI_Transmit(&hspi1, (uint8_t*) datasect, 3, 0xFFFFFFFF);

  HAL_SPI_Receive(&hspi1, (uint8_t*) datasect, len, 0xFFFFFFFF);

  SS_DESELECT();

}

//-----------------------------------------------

uint8_t w5500_readSockBufByte(uint8_t sock_num, uint16_t point)

{

  uint8_t opcode, bt;

  opcode = (((sock_num<<2)|BSB_S0_RX)<<3)|OM_FDM1;

  bt = w5500_readReg(opcode, point);

  return bt;

}

//-----------------------------------------------

void w5500_readSockBuf(uint8_t sock_num, uint16_t point, uint8_t *buf, uint16_t len)

{

  data_sect_ptr *datasect = (void*)buf;

  datasect->opcode = (((sock_num<<2)|BSB_S0_RX)<<3)|OM_FDM0;

  datasect->addr = be16toword(point);

  w5500_readBuf(datasect,len);

}

//-----------------------------------------------

 

The first of these functions reads from the buffer using the variable length data reading method, the second — reads one byte from a buffer of a particular socket, and the third — several bytes from a particular socket.

On the second function — w5500_readSockBufByte — add the prototype in the title file.

Now, in the function of reading and processing network packages, add more code to our condition, which will determine what this is the HTTP request

 

HAL_UART_Transmit(&huart2,(uint8_t*)str1,strlen(str1),0x1000);

w5500_readSockBuf(tcpprop.cur_sock, point, (uint8_t*)tmpbuf, 5);

if (strncmp(tmpbuf,"GET /", 5) == 0)

{

  HAL_UART_Transmit(&huart2,(uint8_t*)"HTTPrn",6,0x1000);

  httpsockprop[tcpprop.cur_sock].prt_tp = PRT_TCP_HTTP;

}

 

If the lines match, we will display the abbreviation "HTTP" in the terminal program, and also initialize the structure field with a specific protocol.

 

 

 

 

 

In file httpd.c add the response function to the HTTP request, in which we find the address of the occurrence of the character "/"

 

extern tcp_prop_ptr tcpprop;

//-----------------------------------------------

void http_request(void)

{

  uint16_t point;

  uint8_t RXbyte;

  uint16_t i=0;

  char *ss1;

  int ch1='.';

 

  // ищем первый "/" в HTTP заголовке

  point = GetReadPointer(tcpprop.cur_sock);

  i = 0;

  while (RXbyte != (uint8_t)'/')

  {

    RXbyte = w5500_readSockBufByte(tcpprop.cur_sock,point+i);

    i++;

  }

}

//-----------------------------------------------

 

 

We create a prototype for this function and call it the function of reading the package in our condition in the file w5500.c

 

httpsockprop[tcpprop.cur_sock].prt_tp = PRT_TCP_HTTP;

http_request();

 

In order not to get confused in the places where the conditions are added, let's leave the bodies of two conditions at once and add two more conditions in advance

 

        http_request();

      }

    }

    else if(httpsockprop[tcpprop.cur_sock].data_stat==DATA_MIDDLE)

    {

      if(httpsockprop[tcpprop.cur_sock].prt_tp == PRT_TCP_HTTP)

      {

      }

    }

    else if(httpsockprop[tcpprop.cur_sock].data_stat==DATA_LAST)

    {

      if(httpsockprop[tcpprop.cur_sock].prt_tp == PRT_TCP_HTTP)

      {

      }

    }

  }

}

 

These conditions will be triggered if one of the — transfer statuses is set to transfer the average package and or transfer the last data package. Similarly, we worked with packages in lessons using the HTTP protocol with the enc28j60 module.

In file httpd.c connect temporary buffer

 

extern char str1[60];

extern char tmpbuf[30];

 

We continue to write the response function to the HTTP request.

We consider the following character after a slash and, if there is a space — then this is a query of the main page. Set the corresponding type of document in the structure field

 

  i++;

}

point+=i;

RXbyte = w5500_readSockBufByte(tcpprop.cur_sock,point);

if(RXbyte==(uint8_t)' ')

{

  strcpy(httpsockprop[tcpprop.cur_sock].fname,"index.htm");

  httpsockprop[tcpprop.cur_sock].http_doc = EXISTING_HTML;

}

else

{

}

 

We will write a body of conditions working otherwise

 

else

{

  // ищем следующий пробел (" ") в HTTP заголовке, таким образом считывая имя документа из запроса

  i=0;

  while (1)

  {

    tmpbuf[i] = w5500_readSockBufByte(tcpprop.cur_sock, point+i);

    if(tmpbuf[i] == (uint8_t)' ') break;

    i++;

  }

  tmpbuf[i] = 0; //закончим строку

  strcpy(httpsockprop[tcpprop.cur_sock].fname,tmpbuf);

}

 

In this body we will get all the bytes until the next space. This will be the name of the document.

We continue the function further. Display the file name that the client requested in the terminal program

 

  strcpy(httpsockprop[tcpprop.cur_sock].fname,tmpbuf);

}

HAL_UART_Transmit(&huart2,(uint8_t*)httpsockprop[tcpprop.cur_sock].fname,strlen(httpsockprop[tcpprop.cur_sock].fname),0x1000);

HAL_UART_Transmit(&huart2,(uint8_t*)"rn",2,0x1000);

 

We collect the code, sew the controller and try to request first the main page, and then some other, then we'll see the result in the terminal

 

image34  image35

 

Everything is read normally.

Now in the file main.c add some global variables to work with the file system, and connect the string array

 

/* Private variables ---------------------------------------------------------*/

extern char SDPath[4]; /* logical drive path */

FATFS SDFatFs;//указатель на объект

extern char str1[60];

/* USER CODE END PV */

 

In function main ( ) also add a local variable for the result

 

/* USER CODE BEGIN 1 */

FRESULT result; //результат выполнения

/* USER CODE END 1 */

 

In the same function, mount our media on the SD map and display the result in the terminal program

 

/* USER CODE BEGIN 2 */

result=f_mount(&SDFatFs,(TCHAR const*)SDPath,0);

sprintf(str1,"f_mount: %drn",result);

HAL_UART_Transmit(&huart2,(uint8_t*)str1,strlen(str1),0x1000);

net_ini();

 

We collect the code, sew the controller and see the result in the terminal

 

image36

 

Everything was finely mounted.

Let's go back to the file httpd.c and add the necessary global variables for working with the file system

 

extern tcp_prop_ptr tcpprop;

FIL MyFile;

FRESULT result; //результат выполнения

uint32_t bytesread;

//-----------------------------------------------

 

Continue our HTTP request response feature.

We open the required file from the card and find out its size. We will also show this in the terminal program

 

HAL_UART_Transmit(&huart2,(uint8_t*)"rn",2,0x1000);

f_close(&MyFile);

result=f_open(&MyFile,httpsockprop[tcpprop.cur_sock].fname,FA_READ); //Попытка открыть файл

sprintf(str1,"f_open: %drn",result);

HAL_UART_Transmit(&huart2,(uint8_t*)str1,strlen(str1),0x1000);

sprintf(str1,"f_size: %lurn",MyFile.fsize);

HAL_UART_Transmit(&huart2,(uint8_t*)str1,strlen(str1),0x1000);

 

Before opening the file, we close the previous one. You can do this in other places, but it works best for me.

We’ll collect the project again, we’ll send a controller and try to request some existing document

 

image37

 

The size is determined normally.

I created several html pages of various sizes to test our project with the request of documents consisting of different numbers of packages.

Add a few types of HTTP headers for response in the form of global arrays, as well as an array with a page transmitted in case of absence of the requested document

 

uint32_t bytesread;

//-----------------------------------------------

const char http_header[] = { "HTTP/1.1 200 OKrnContent-Type: text/htmlrnrn"};

const char jpg_header[] = {"HTTP/1.0 200 OKrnServer: nginxrnContent-Type: image/jpegrnConnection: closernrn"};

const char icon_header[] = { "HTTP/1.1 200 OKrnContent-Type: image/x-iconrnrn"};

const char error_header[] = {"HTTP/1.0 404 File not foundrnServer: nginxrnContent-Type: text/htmlrnConnection: closernrn"};

char *header;

//-----------------------------------------------

const uint8_t e404_htm[] = {

0x3c,0x68,0x74,0x6d,0x6c,0x3e,0x0a,0x20,0x20,0x3c,0x68,0x65,0x61,0x64,0x3e,0x0a,

0x20,0x20,0x20,0x20,0x3c,0x74,0x69,0x74,0x6c,0x65,0x3e,0x34,0x30,0x34,0x20,0x4e,

0x6f,0x74,0x20,0x46,0x6f,0x75,0x6e,0x64,0x3c,0x2f,0x74,0x69,0x74,0x6c,0x65,0x3e,

0x0a,0x20,0x20,0x3c,0x2f,0x68,0x65,0x61,0x64,0x3e,0x0a,0x3c,0x62,0x6f,0x64,0x79,

0x3e,0x0a,0x3c,0x68,0x31,0x20,0x73,0x74,0x79,0x6c,0x65,0x3d,0x22,0x74,0x65,0x78,

0x74,0x2d,0x61,0x6c,0x69,0x67,0x6e,0x3a,0x20,0x63,0x65,0x6e,0x74,0x65,0x72,0x3b,

0x22,0x3e,0x34,0x30,0x34,0x20,0x45,0x72,0x72,0x6f,0x72,0x20,0x46,0x69,0x6c,0x65,

0x20,0x4e,0x6f,0x74,0x20,0x46,0x6f,0x75,0x6e,0x64,0x3c,0x2f,0x68,0x31,0x3e,0x0a,

0x3c,0x68,0x32,0x20,0x73,0x74,0x79,0x6c,0x65,0x3d,0x22,0x74,0x65,0x78,0x74,0x2d,

0x61,0x6c,0x69,0x67,0x6e,0x3a,0x20,0x63,0x65,0x6e,0x74,0x65,0x72,0x3b,0x22,0x3e,

0x20,0x54,0x68,0x65,0x20,0x70,0x61,0x67,0x65,0x20,0x79,0x6f,0x75,0x20,0x61,0x72,

0x65,0x20,0x6c,0x6f,0x6f,0x6b,0x69,0x6e,0x67,0x20,0x66,0x6f,0x72,0x20,0x6d,0x69,

0x67,0x68,0x74,0x20,0x68,0x61,0x76,0x65,0x20,0x62,0x65,0x65,0x6e,0x20,0x72,0x65,

0x6d,0x6f,0x76,0x65,0x64,0x2c,0x20,0x3c,0x62,0x72,0x20,0x2f,0x3e,0x68,0x61,0x64,

0x20,0x69,0x74,0x73,0x20,0x6e,0x61,0x6d,0x65,0x20,0x63,0x68,0x61,0x6e,0x67,0x65,

0x64,0x2c,0x20,0x6f,0x72,0x20,0x69,0x73,0x20,0x74,0x65,0x6d,0x70,0x6f,0x72,0x61,

0x72,0x69,0x6c,0x79,0x20,0x75,0x6e,0x61,0x76,0x61,0x69,0x6c,0x61,0x62,0x6c,0x65,

0x2e,0x3c,0x2f,0x68,0x32,0x3e,0x0a,0x3c,0x2f,0x62,0x6f,0x64,0x79,0x3e,0x3c,0x2f,

0x68,0x74,0x6d,0x6c,0x3e};

//-----------------------------------------------

 

We continue to write our response function to the HTTP request. If the document has opened, then we will study its extension and set the necessary status, and if not, we will establish the status of the error of the existence of the document on the server. We also calculate in advance the length of the HTTP response in order to determine later what many packages and windows it will fit

 

HAL_UART_Transmit(&huart2,(uint8_t*)str1,strlen(str1),0x1000);

if (result==FR_OK)

{

  //изучим расширение файла

  ss1 = strchr(httpsockprop[tcpprop.cur_sock].fname,ch1);

  ss1++;

  if (strncmp(ss1,"jpg", 3) == 0)

  {

    httpsockprop[tcpprop.cur_sock].http_doc = EXISTING_JPG;

    //сначала включаем в размер размер заголовка

    httpsockprop[tcpprop.cur_sock].data_size = strlen(jpg_header);

  }

  if (strncmp(ss1,"ico", 3) == 0)

  {

    httpsockprop[tcpprop.cur_sock].http_doc = EXISTING_ICO;

    //сначала включаем в размер размер заголовка

    httpsockprop[tcpprop.cur_sock].data_size = strlen(icon_header);

  }

  else

  {

    httpsockprop[tcpprop.cur_sock].http_doc = EXISTING_HTML;

    //сначала включаем в размер размер заголовка

    httpsockprop[tcpprop.cur_sock].data_size = strlen(http_header);

  }

  //затем размер самого документа

  httpsockprop[tcpprop.cur_sock].data_size += MyFile.fsize;

}

else

{

  httpsockprop[tcpprop.cur_sock].http_doc = E404_HTML;

  //сначала включаем в размер размер заголовка

  httpsockprop[tcpprop.cur_sock].data_size = strlen(error_header);

  //затем размер самого документа

  httpsockprop[tcpprop.cur_sock].data_size += sizeof(e404_htm);

}

 

B next part lesson we will continue the response function to the HTTP request, as well as write the function of transferring to the client a document that fits with the heading in one window.

 

Documents
Comments Write