---
title: "STM Lesson 91. LAN. W5500. HTTP Server. Part 4"
url: "https://maker.wiznet.io/teddy/projects/stm-lesson-91-lan-w5500-http-server-part-4/"
markdown_url: "https://maker.wiznet.io/teddy/projects/stm-lesson-91-lan-w5500-http-server-part-4/md"
type: "UCC: User Created Content"
author: "Narod stream"
author_url: "https://narodstream.ru/stm-urok-91-lan-w5500-http-server-chast-4/"
editor: "WIZnet"
editor_url: "https://maker.wiznet.io/"
original_author: "Narod stream"
original_url: "https://narodstream.ru/stm-urok-91-lan-w5500-http-server-chast-4/"
published: "2023-08-02"
language: "en"
likes: 0
views: 393
comments: 0
source: "WIZnet Makers (https://maker.wiznet.io/)"
---

# STM Lesson 91. LAN. W5500. HTTP Server. Part 4

> STM Lesson 91. LAN. W5500. HTTP Server. Part 4

Original author: Narod stream (source: https://narodstream.ru/stm-urok-91-lan-w5500-http-server-chast-4/)

## Article

**Part 4**

## **LAN. W5500. HTTP server**

B [**previous part**](https://narodstream.ru/stm-urok-91-lan-w5500-http-server-chast-3/) lesson we examined the HTTP request that came from the client and began to form an answer to it.

Add a global variable for window size

`uint32_t``bytesread;`

`**volatile**````**uint16_t**``** tcp_size_wnd = 2048;**`

The size of the window for the socket — is the size of its buffer. But since we don’t touch the size of the boufers of the sockets and they all silently make up 2 kilobytes, then we are enough for the size of the window of one variable.

And the maximum size of the default segment is also determined, in my opinion 1460, it can also be changed at any time. To do this, there is a special register in the chip.

We continue the response function to the HTTP request. We consider the number of windows ( we do not need to consider the number of segments, the buffer itself shares the segments of the chip without our participation ).

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

`}`

`**httpsockprop[tcpprop.cur_sock].cnt_rem_data_part = httpsockprop[tcpprop.cur_sock].data_size / tcp_size_wnd + 1;**`

`**httpsockprop[tcpprop.cur_sock].last_data_part_size = httpsockprop[tcpprop.cur_sock].data_size % tcp_size_wnd;**`

`**//борьба с неправильным расчётом, когда общий размер делится на минимальный размер окна без остатка**`

`**if**``**(httpsockprop[tcpprop.cur_sock].last_data_part_size==0)**`

`**{**`

`** httpsockprop[tcpprop.cur_sock].last_data_part_size=tcp_size_wnd;**`

`** httpsockprop[tcpprop.cur_sock].cnt_rem_data_part--;**`

`**}**`

`**httpsockprop[tcpprop.cur_sock].cnt_data_part = httpsockprop[tcpprop.cur_sock].cnt_rem_data_part;**`

`**sprintf(str1,**``**"data size:%lu; cnt data part:%u; last_data_part_size:%urn"**``**,**`

`**(**``**unsigned**````**long**``**)httpsockprop[tcpprop.cur_sock].data_size, httpsockprop[tcpprop.cur_sock].cnt_rem_data_part, httpsockprop[tcpprop.cur_sock].last_data_part_size);**`

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

The whole calculation is similar to the calculation that we used to use when working with the previous module, so he does not need an explanation, I think.

We collect the code, sew the controller and request a bigger dock

![image38](https://narodstream.ru/wp-content/uploads/2017/09/image38.png)

Everything is considered. Fine! Check, I think the calculation is not necessary. Since I have done this many times before giving a lesson. If something goes wrong, then we will see it later in the form of a distorted document in the browser.

Depending on the result, set the status of data transfer

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

`**if**``** (httpsockprop[tcpprop.cur_sock].cnt_rem_data_part==1)**`

`**{**`

`** httpsockprop[tcpprop.cur_sock].data_stat = DATA_ONE;**`

`**}**`

`**else**````**if**``** (httpsockprop[tcpprop.cur_sock].cnt_rem_data_part>1)**`

`**{**`

`** httpsockprop[tcpprop.cur_sock].data_stat = DATA_FIRST;**`

`**}**`

Everything is simple here. If we have an HTTP response that includes a document along with the heading, ( buffer ) fits in one window, then this is one status. And if not — then another.

And now, based on these statuses, we will cause certain functions. For now, we will only add conditions

`httpsockprop[tcpprop.cur_sock].data_stat = DATA_FIRST;`

`}`

`**if**``**(httpsockprop[tcpprop.cur_sock].data_stat==DATA_ONE)**`

`**{**`

`**}**`

`**else**````**if**``**(httpsockprop[tcpprop.cur_sock].data_stat==DATA_FIRST)**`

`**{**`

`**}**`

Add above the function of sending a document that fits into a single package along with the heading

`**//-----------------------------------------------**`

`**void**``** tcp_send_http_one(**``**void**``**)**`

`**{**`

`** uint16_t**``** i=0;**`

`** uint16_t**``** data_len=0;**`

`** uint16_t**``** header_len=0;**`

`** uint16_t**``** end_point;**`

`** uint8_t**``** num_sect=0;**`

`** uint16_t**``** len_sect;**`

`**}**`

`**//-----------------------------------------------**`

In file **w5500.c** after the SocketListenWait function, add a couple more functions

`**//-----------------------------------------------**`

`**void**``** SocketClosedWait(**``**uint8_t**``** sock_num)**`

`**{**`

`** uint8_t**``** opcode=0;**`

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

`** while**``**(1)**`

`** {**`

`** if**``**(w5500_readReg(opcode, Sn_SR)==SOCK_CLOSED)**`

`** {**`

`** break**``**;**`

`** }**`

`** }**`

`**}**`

`**//-----------------------------------------------**`

`**void**``** DisconnectSocket(**``**uint8_t**``** sock_num)**`

`**{**`

`** uint8_t**``** opcode=0;**`

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

`** w5500_writeReg(opcode, Sn_CR, 0x08); **``**//DISCON**`

`**}**`

`**//-----------------------------------------------**`

The first function is waiting for the socket to close, and the second — gives the command to disconnect.

Add prototypes to function data in the title file, return to the file **httpd.c** and we will process the condition for the transfer of the document together with the heading consisting of a single window

`if``(httpsockprop[tcpprop.cur_sock].data_stat==DATA_ONE)`

`{`

`** tcp_send_http_one();**`

`** DisconnectSocket(tcpprop.cur_sock); **``**//Разъединяемся**`

`** SocketClosedWait(tcpprop.cur_sock);**`

`** OpenSocket(tcpprop.cur_sock,Mode_TCP);**`

`** //Ждём инициализации сокета (статус SOCK_INIT)**`

`** SocketInitWait(tcpprop.cur_sock);**`

`** //Продолжаем слушать сокет**`

`** ListenSocket(tcpprop.cur_sock);**`

`** SocketListenWait(tcpprop.cur_sock);**`

`}`

Although the function of sending a document is still empty, but, nevertheless, we will collect the code, we will open the controller and see what changes we have. Of course we will ask for a document that will fit into one package

The result in our terminal program is similar to the previous one, only the difference is that it is displayed only once, that is, the code is no longer fixated with us.

We will also see the result in the traffic analyzer

![image39](https://narodstream.ru/wp-content/uploads/2017/09/image39-1.png)

To request a document, we simply close the connection in the correct way. This is good.

Now continue to write the function **tcp_send_http_one**.

Depending on the type of document, set the HTTP header to the desired array. Moreover, we will do all this when fulfilling the conditions of existence of the document

uint16_t len_sect;
`**if**``** ((httpsockprop[tcpprop.cur_sock].http_doc==EXISTING_HTML)||**`

`** (httpsockprop[tcpprop.cur_sock].http_doc==EXISTING_JPG)||**`

`** (httpsockprop[tcpprop.cur_sock].http_doc==EXISTING_ICO))**`

`**{**`

`** switch**``**(httpsockprop[tcpprop.cur_sock].http_doc)**`

`** {**`

`** case**``** EXISTING_HTML:**`

`** header = (**``**void**``***)http_header;**`

`** break**``**;**`

`** case**``** EXISTING_ICO:**`

`** header = (**``**void**``***)icon_header;**`

`** break**``**;**`

`** case**``** EXISTING_JPG:**`

`** header = (**``**void**``***)jpg_header;**`

`** break**``**;**`

`** }**`

`**}**`

`**else**`

`**{**`

`**}**`

In file **w5500.c** add two more functions over the initialization function

`**//-----------------------------------------------**`

`**uint16_t**``** GetWritePointer(**``**uint8_t**``** sock_num)**`

`**{**`

`** uint16_t**``** point;**`

`** uint8_t**``** opcode;**`

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

`** point = (w5500_readReg(opcode,Sn_TX_WR0)<<8|w5500_readReg(opcode,Sn_TX_WR1));**`

`** return**``** point;**`

`**}**`

`**//-----------------------------------------------**`

`**void**``** SetWritePointer(**``**uint8_t**``** sock_num, **``**uint16_t**``** point)**`

`**{**`

`** uint8_t**``** opcode;**`

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

`** w5500_writeReg(opcode, Sn_TX_WR0, point>>8);**`

`** w5500_writeReg(opcode, Sn_TX_WR1, (**``**uint8_t**``**)point);**`

`**}**`

`**//-----------------------------------------------**`

The first of the functions will return the data start address to write to the shipping buffer, and the second — install it.

Add to the data of the prototype function and continue to write the function **tcp_send_http_one** in file **httpd.c**, continuing the body of our condition

`break``;`

`}`

`**header_len = **``**strlen**``**(header);**`

`**data_len = (**``**uint16_t**``**)MyFile.**``**fsize**``**;**`

`**end_point = GetWritePointer(tcpprop.**``**cur_sock**``**);**`

`**end_point+=header_len+data_len;**`

In this code we find out the size of the title, data, also the start address of the record. Then we set the address with the added data size and title and read it again.

Although I checked that the previous address is read without adding data size and title, but nonetheless does not work differently.

I also want to note that all the buffers are ring and if the size on the middle address suddenly ends, the data is written and read from zero. No interference on our part is required in this process, the chip will do everything on its own.

In file **w5500.c** add another global array for the sector, which we will then tie to a certain structure

`char``tmpbuf[30];`

`**uint8_t**``** sect[515];**`

Also in this file after function **w5500_writeReg** add two more pounds

`**//-----------------------------------------------**`

`**void**``** w5500_writeBuf(data_sect_ptr *datasect, **``**uint16_t**``** len)**`

`**{**`

`** SS_SELECT();**`

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

`** SS_DESELECT();**`

`**}**`

`**//-----------------------------------------------**`

`**void**``** w5500_writeSockBuf(**``**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_TX)<<3)|(RWB_WRITE<<2)|OM_FDM0;**`

`** datasect->addr = be16toword(point);**`

`** w5500_writeBuf(datasect,len+3);**``**//3 служебных байта**`

`**}**`

`**//-----------------------------------------------**`

One of these functions will write data of variable length in the buffer, and the other will do the same only with reference to a particular socket.

And we tie the buffer to the structure **datasect**, the first 2 bytes of which contain the memory address ( register ), first with the senior byte, and then the youngest, and the third byte — is opcode. Therefore, we must prepare all this in advance.

In the second function, we do this. We swap bays in the address using macro **be16toword**. And we prepare the data ourselves in the buffer before calling this function, and for convenience immediately from the address of the fourth byte.

Create a prototype in the header file for the second function, return to the file **httpd.c** and connect our buffer there

`extern``char``tmpbuf[30];`

`**extern**````**uint8_t**``** sect[515];**`

We will return to our function, fill out the buffer with header data, write it to the shipping buffer and move the pointer

`end_point+=header_len+data_len;`

`**//Заполним данными буфер для отправки пакета**`

`**SetWritePointer(tcpprop.cur_sock, end_point);**`

`**end_point = GetWritePointer(tcpprop.**``**cur_sock**``**);**`

`**memcpy(sect+3,header,header_len);**`

`**w5500_writeSockBuf(tcpprop.cur_sock, end_point, (uint8_t*)sect, header_len);**`

`**end_point+=header_len;**`

Here we are just filling out the buffer with data from the address of the fourth byte.

Then we calculate the number of sectors of 512 bytes, add a loop in which we read them from the SD card and write them to the transfer buffer

`end_point+=header_len;`

`**num_sect = data_len / 512;**`

`** for**``**(i=0;i<=num_sect;i++)**`

`** {**`

`** //не последний сектор**`

`** if**``**(i<(num_sect-1)) len_sect=512;**`

`** else**``** len_sect=data_len;**`

`** result=f_lseek(&MyFile,i*512); **``**//Установим курсор чтения в файле**`

`** sprintf(str1,**``**"f_lseek: %drn"**``**,result);**`

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

`** result=f_read(&MyFile,sect+3,len_sect,(UINT *)&bytesread);**`

`** w5500_writeSockBuf(tcpprop.cur_sock, end_point, (uint8_t*)sect, len_sect);**`

`** end_point+=len_sect;**`

`** data_len -= len_sect;**`

`** }**`

`}`

`else`

Let's go to the file **w5500.c** and add two more functions there to send the package after the function **GetSocketStatus**

`**//-----------------------------------------------**`

`**void**``** RecvSocket(**``**uint8_t**``** sock_num)**`

`**{**`

`** uint8_t**``** opcode;**`

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

`** w5500_writeReg(opcode, Sn_CR, 0x40); **``**//RECV SOCKET**`

`**}**`

`**//-----------------------------------------------**`

`**void**``** SendSocket(**``**uint8_t**``** sock_num)**`

`**{**`

`** uint8_t**``** opcode;**`

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

`** w5500_writeReg(opcode, Sn_CR, 0x20); **``**//SEND SOCKET**`

`**}**`

`**//-----------------------------------------------**`

The first function prepares the shipment by moving all the buffer pointers, and the second directly sends the buffer to the network device with which the connection is created.

We will create prototypes for these functions and return to our unsigned function in a file **httpd.c**.

While the nasty part of our condition, in which we will send an answer with an error, we will skip and at the end of the function we will send our buffer

`else`

`{`

`}`

`** //отправим данные**`

`** RecvSocket(tcpprop.cur_sock);**`

`** SendSocket(tcpprop.cur_sock);**`

`** httpsockprop[tcpprop.cur_sock].data_stat = DATA_COMPLETED;**`

`}`

Let's collect the code, send the controller, then try to first request a document that fits not only in one buffer ( window ), but even in one segment

![image40](https://narodstream.ru/wp-content/uploads/2017/09/image40.png)

We see that the document was normally transmitted. The icon that I put into the document was also transmitted.

Let's see the traffic analyzer

![image41](https://narodstream.ru/wp-content/uploads/2017/09/image41.png)

Everything is also fine here.

Now we ask for a page that will fit into one window ( buffer ), but not in the — segment.

She was also transmitted, I think it makes no sense to show. Let's see only the conclusion in WireShark

![image42](https://narodstream.ru/wp-content/uploads/2017/09/image42.png)

We see that here the process is going right. Everything was transmitted and broken into segments. All confirmations received. Connection created and torn on time.

Let's move on to a bad case now and do the same with the header array for error and the array of the transmitted page document with an error

`data_len -= len_sect;`

`}`

`}`

`else`

`{`

`** header_len = strlen(error_header);**`

`** data_len = **``**sizeof**``**(e404_htm);**`

`** end_point = GetWritePointer(tcpprop.cur_sock);**`

`** end_point+=header_len+data_len;**`

`** SetWritePointer(tcpprop.cur_sock, end_point);**`

`** end_point = GetWritePointer(tcpprop.cur_sock);**`

`** //Заполним данными буфер для отправки пакета**`

`** memcpy(sect+3,error_header,header_len);**`

`** w5500_writeSockBuf(tcpprop.cur_sock, end_point, (uint8_t*)sect, header_len);**`

`** end_point+=header_len;**`

`** memcpy(sect+3,e404_htm,data_len);**`

`** w5500_writeSockBuf(tcpprop.cur_sock, end_point, (uint8_t*)sect, data_len);**`

`** end_point+=data_len;**`

`}`

`//отправим данные`

The code here is similar to the code of the true body and there is nothing to explain here, I think.

We collect the code, send the controller and try to request a non-existent document

![image43](https://narodstream.ru/wp-content/uploads/2017/09/image43.png)

The page with an error and its title is transferred. Also see you with WireShark

![image44](https://narodstream.ru/wp-content/uploads/2017/09/image44.png)

B [**next part**](https://narodstream.ru/stm-urok-91-lan-w5500-http-server-chast-5/) lesson we will write three more functions for transferring a large document and thereby complete the writing of the code of our server.

[Embedded media](https://www.youtube.com/embed/mnoBjFmLVCA)

---

Source: https://maker.wiznet.io/teddy/projects/stm-lesson-91-lan-w5500-http-server-part-4/
