STM Урок 136. LAN8742A. LWIP. SOCKET. HTTP Server



Продолжаем работу со стеком протоколов LWIP, а также с его интерфейсом SOCKET.

И сегодня мы начнём работать с протоколом уже прикладного уровня — HTTP. Данный протокол находится выше уровнем, чем протокол TCP, а протокол TCP является для протокола HTTP протоколом транспортного уровня. Со всем этим мы уже знакомы, так как с протоколом HTTP мы работаем давно и нет смысла углубляться в его особенности, а также в то, насколько в нынешний век интернета он востребован.

И скорее всего, мы также пойдём последовательным путём, как и в случае использования интерфейса NETCONN, хотя в данный момент всё это не так актуально. Уроки по стеку протоколов LWIP нацелены больше на будущее, так как не все посетители ещё дошли до данной темы. Но у меня уже проект отработан, поэтому я всё-таки помимо урока по упрощённой работе с протоколом HTTP дам пару уроков и по использованию технологий AJAX и Websocket. Что предусматривают данные технологии, можете почитать в уроках по NETCONN.

А сейчас проект, который был сделан из проекта урока 133 с именем LAN8742_TCP_SERVER_SOCKET и имя ему было дано LAN8742_HTTP_SERVER_SOCKET.

Запустим проект в Cube MX и перейдём в настройку LWIP. В разделе HTTPD включим сервер

 

 

Сохраним настройки, сгенерируем проект и откроем его в среде System Workbench.

Зайдём в настройки проекта, уровень оптимизации установим в 1 и удалим при наличии отладочные настройки.

Перейдём в файл main.c и сначала уберём так же, как и в уроке 134, массивы на SDRAM.

Удалим здесь

 

unsigned char *out_buffer;

char *str_usart;

char *str_out;

unsigned char *send_ws_buf;

char* str_buf1;

char* str_buf2;

char* str_buf3;

 

Вместо этого добавим глобальные массивы в обычной ОЗУ

 

 

Также в функции main() удалим инициализацию массивов в памяти SDRAM

 

//str_usart 512 bytes

str_usart = (char *) 0xC0400000;

//str_out 32 bytes

str_out = (char *) 0xC0400200;

//out_buffer 192 bytes

out_buffer = (uint8_t *) 0xC0400220;

memset(out_buffer,0,192);

//send_ws_buf 1312 bytes

send_ws_buf = (uint8_t *)0xC04002E0;

//str_buf1 256 bytes

str_buf1 = (char *) 0xC0400800;

//str_buf2 256 bytes

str_buf2 = (char *) 0xC0400900;

//str_buf2 1024 bytes

str_buf3 = (char *) 0xC0400A00;

 

В функции main() исправим первым делом шапку проекта

 

TFT_DisplayString(0, 10, (uint8_t *)"HTTP Server", CENTER_MODE);

 

Как мы помним, проект у нас не сможет собраться из-за отсутствия файла fsdata.c, который мы хорошо помним, как надо генерировать, так как с сервером HTTP нам работать не привыкать. Весь состав документального контента мы возьмём тот же, что и в уроке 127. Скопируем папку fs, сохранённую для данного урока, вместе с содержимым, а также файлы makefsdata.exe, makefsdata.cmd и msvcr100d.dll, чтобы мы могли свободно собирать недостающий файл с контентом, в папку «Папка с проектом//Middlewares/Third_Party/LwIP/src/apps/httpd/». Также в папку fs помести файл favicon.ico, который вы сами себе сгенерируйте под себя. Это иконка сайта, которая отображается в закладке с браузером вместе с именем документа. Если этот файл поместить, то серверу не придется постоянно отвечать браузерам об его отсутствии. Для генерации данного файла существует ряд онлайн-сервисов.

Соберём с помощью файла makefsdata.cmd файл fsdata.c и вернёмся в проект.

Сделаем проекту Refresh и отключим от компиляции наш файл fsdata.c.

Попробуем теперь собрать проект, проект должен будет нормально собраться.

Даже если проект соберётся нормально, с виртуальной файловой системой мы работать не сможем.

Для этого надо будет подключить библиотеку

 

 

Уберём объявление глобальных структур и переменных их типов, которые нам сегодня не потребуются

 

typedef struct struct_conn_t {

uint32_t conn;

uint8_t type;

} struct_conn;

struct_conn conn01;

typedef struct struct_client_socket_t {

struct sockaddr_in remotehost;

socklen_t sockaddrsize;

int accept_sock;

uint16_t y_pos;

} struct_client_socket;

struct_client_socket client_socket01;

 

Удалим также функцию client_socket_thread со всем содержимым.

 

 

Мы не будем делать отдельные задачи под соединения, так как в этом случае непременно будут глюки. Я пробовал. Обойдёмся одной задачей на всё, как мы это делали в случае использования API NETCONN.

 

Также в функции задачи вывода строк на дисплей очистим память очереди

 

 

В функции задачи по умолчанию StartDefaultTask исправим номер порта

 

sock01.port = 80;

 

Также при создании задачи для сокета увеличим стек

 

sys_thread_new("tcp_thread", tcp_thread, (void*)&sock01, DEFAULT_THREAD_STACKSIZE * 4, osPriorityNormal);

 

В функции tcp_thread создадим массив структур для передачи в очередь строки в целях последующего вывода её на дисплее.

Мы будем использовать для каждой строки отдельную структуру, иначе наши строки потеряются при удалении элемента из очереди

 

 

Добавим ещё одну локальную переменную

 

int sock, accept_sock, ret;

 

Добавим буфер и переменную для хранения длины буфера

 

 

Объявим переменную файловой структуры

 

 

 

Немого добавим количество элементов в очередь для прослушивания

 

listen(sock, 8);

 

Уберём вывод в терминальную программу

 

sprintf(str_usart,» socket: %d\r\n», accept_sock);

HAL_UART_Transmit(&huart1,(uint8_t*)str_usart,strlen(str_usart),0x1000);

 

Удалим тело следующего условия

 

if(accept_sock >= 0)

{

  client_socket01.accept_sock = accept_sock;

  client_socket01.remotehost = remotehost;

  client_socket01.sockaddrsize = sockaddrsize;

  client_socket01.y_pos = arg_sock->y_pos;

  sys_thread_new(«client_socket_thread», client_socket_thread, (void*)&client_socket01, DEFAULT_THREAD_STACKSIZE, osPriorityNormal );

}

 

Попытаемся принять пакет от клиента, если в нём есть данные, то заготовим под этот вариант условие с телом, и затем, независимо от выполнения условия, закроем соединение с клиентом

 

 

В теле условия добавим ещё одно условие, проверяющее то, что пришел именно запрос HTTP

 

 

Если пришёл запрос HTTP, выведем надпись внизу дисплея

 

 

Ответим на запрос аналогичным образом, как мы делали в уроке по API NETCONN

 

 

Если строка запроса больше 20 символов, то ограничим её для вывода на дисплей

 

 

Выведем строку на дисплей, а ниже — минимальный размер кучи

 

 

Подключим нашу плату STM32F746G-DISCOVERY к ПК посредством кабеля mini-USB, также подключим плату к сети, соберём проект и прошьём контроллер.

Сначала запустим анализатор сетевого трафика Wireshark и отфильтруем его по адресу нашего сервера HTTP.

Запустим WEB-браузер (я использую для этих целей Mozilla Firefox, он мне кажется как-то стабильнее что-ли да и привык я к его отладке) и введём пока IP-адрес нашего сервера без наименования документа.

Должен будет отобразиться документ index.html

 

 

Также посмотрим, что у нас всё нормально в Wireshark

 

 

Теперь попробуем запросить страниц посложнее, с картинками — index1.html

 

 

 

Также прекрасно выводится весь документ целиком, картинка ни одна не теряется.

На дисплее также всё отображается, что говорит о том, что в очереди в одной функции при выводе разных строк, если ожидается вывод их друг за другом, всё-таки целесообразнее использовать под каждый вывод строки свою отдельную структуру

 

 

Попробуем сделать запрос несуществующего документа

 

 

Браузер отреагировал правильно, предоставив нам страницу с ошибкой.

На дисплее также появилась соответствующая надпись

 

 

Отлично!

Итак, воспользовавшись нашим накопленным опытом, а также удобством интерфейса SOCKET стека протоколов LWIP, мы, можно сказать, не прикладывая каких-то особых усилий, создали несложный, но очень уверенно функционирующий сервер HTTP.

Всем спасибо за внимание!

 

 

Предыдущий урок Программирование МК STM32 Следующий урок

 

Исходный код

 

Архив файлов для сервера

 

Отладочную плату можно приобрести здесь 32F746G-DISCOVERY

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

STM LAN8742A. LWIP. SOCKET. HTTP Server

2 комментария на “STM Урок 136. LAN8742A. LWIP. SOCKET. HTTP Server
  1. Кирилл:

    Добрый день!
    При попытке компиляции возникают ошибки с этими строчками:

    __weak void configureTimerForRunTimeStats(void)
    {

    }

    __weak unsigned long getRunTimeCounterValue(void)
    {

    Если перенести '♯ include' stm32f7xx_hal.h 'из main.c в main.h., то проблема пропадает и все запускается, однако после сервер на STM не подает признаков жизни и ничего не работает.

    Можно ли этот как-то починить?

  2. sunilvigneshnehru:

    HI,
    I have try your code in NUCLEO — F746ZG. there sDRAM not accessible and no TFT display.
    and in freeRTOS handle
    sys_thread_new(«tcp_thread», tcp_thread, (void*)&sock01, DEFAULT_THREAD_STACKSIZE * 4, osPriorityNormal);
    is not working.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*