Найти в Дзене
narod stream

ESP32 Урок 26. Wi-Fi. STA. TCP Server

Продолжаем работу по программированию микроконтроллера ESP8266 с использованием операционной системы реального времени FREEFTOS, а также продолжаем работу с протоколом TCP (Transmission Control Protocol). И на данном уроке мы уже попытаемся создать простенький TCP сервер, который позволит нам обрабатывать пришедшие пакеты от клиентов, а также отвечать на них. Напомню также, что мы также этим раньше подобные задачи решали с использованием других контроллеров, поэтому нам будет гораздо легче справиться с нашей задачей.

Схема наша осталась прежняя

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

Откроем наш проект в Espressif IDE и в файле tcp.c удалим для начала функцию recv_task вместе с её телом.

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

В функции tcp_task удалим следующие строки

//Заполнение информации о клиенте
cliaddr.sin_family = AF_INET; // IPv4
cliaddr.sin_addr.s_addr = INADDR_ANY;
cliaddr.sin_port = htons(CONFIG_CLIENT_PORT);

Строки с созданием очередей также удалим

xQueueClose = xQueueCreate(10, sizeof(unsigned char));
xQueueCloseAsk = xQueueCreate(10, sizeof(unsigned char));

Изменим адрес сервера в данной строке, так как данный адрес может быть разным и это адрес нашего узла

servaddr.sin_addr.s_addr = INADDR_ANY;

Исправим также комментарий

//Свяжем сокет с адресом сервера

Здесь также исправим имя переменной

if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(struct sockaddr_in)) < 0 )

Выше удалим строку с инициализацией памяти под структуру адреса клиента

memset(&cliaddr, 0, sizeof(cliaddr));

Удалим следующие строки:

if (connect(sockfd, (struct sockaddr *)&servaddr,sizeof(struct sockaddr_in)) >= 0)
{
  sprintf(str1, "Connected");
  xQueueSendToBack(lcd_string_queue, &xLCDData, 0);
  recv_socket01.y_pos = 2;
  recv_socket01.sock = sockfd;
  xTaskCreate(recv_task, "recv_task", 2048, (void*)&recv_socket01, 3, &xRecvTaskHandle);
  vTaskDelay( 2000 / portTICK_RATE_MS);
  for(int i=1; i<10; i++)
  {
    snprintf(str1, sizeof(str1), "Hello from ESP!!!\n");
    write(sockfd,(void *) str1,strlen(str1));
    vTaskDelay( 2000 / portTICK_RATE_MS);
  }
  char fl = 1;
  xQueueSendToBack(xQueueClose, &fl, 0);
  for(;;)
  {
    xQueueReceive(xQueueCloseAsk, &fl, 0);
    if(fl==1)
    {
      ESP_LOGI(TAG, "task delete\n");
      break;
    }
    vTaskDelay( 10 / portTICK_RATE_MS);
  }
}

После связи сокета с адресом сервера начнём слушать наш сокет

Напомню, что цифра во втором параметре — это максимальное количество попыток соединений в очереди.

Объявим локальную переменную для идентификации сокета, который будет использоваться для клиента, пытающегося соединиться с нашим сервером

int sockfd, accept_sock;

Объявим также переменную для хранения размера адреса сокета

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

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

Вернёмся в нашу задачу tcp_task и в бесконечном цикле покажем в терминале значение переменной идентификатора сокета

Объявим переменную для горизонтальной позиции на дисплее

Удалим объявление хендла задачи приёма пакетов

TaskHandle_t xLCDTaskHandle = NULL, xRecvTaskHandle = NULL;

Проинициализируем поля переменной и создадим задачу, передав в качестве параметра нашу переменную

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

Присвоим адрес параметров задачи объявленному указателю

Объявим переменную структуры адреса сокета, а также размера данного адреса

Объявим переменную длины буфера и сразу инициализируем её

Объявим и инициализируем массив для хранения данных буфера

Инициализируем поля переменной структуры для задачи дисплея

Присвоим адрес строки массиву в структуре дисплея

Присвоим идентификатор сокета локальной переменной

Аналогично инициализируем другие переменные

Добавим бесконечный цикл, в котором попытаемся принять пакет от клиента

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

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

А если пришел непустой пакет, то скопируем данные из буфера в строковую переменную, остаток до 20 забьём пробелами, завершим нулём и отправим на дисплей в нужную позицию, соответствующую идентификатору (номеру) сокета. Также отправим строку с данными в терминальную программу и обратно клиенту

Ну, вроде всё. Наконец-то настал час испытания нашего проекта.

Соберём его и прошьём контроллер.

В качестве программы-клиента будем использовать программу Putty на компьютере.

Также запустим анализатор пакетов WireShark, в котором отфильтруемся по адресу нашей платы, который мы увидим в терминале при загрузке программы в контроллере.

Также в терминале мы можем видеть, что мы создали сокет и связали с ним адрес сервера

-2

Попытаемся соединиться с нашим сервером

-3

Увидим в Wireshark, что соединение удалось

-4

В терминале мы также видим, что сокет валидный

-5

Попробуем что-нибудь передать нашей плате

-6

Мы видим, что пакет нам вернулся назад, отлично. Также мы видим, что пакеты успешно передаются и принимаются в анализаторе трафика

-7

В терминале мы также видим строку и номер сокета

-8

На дисплее мы также видим нашу строку

-9

Давайте запустим ещё 3 клиента, благо памяти у нашего ESP32 побольше, чем у ESP8266

-10

У нас создались 3 новых сокета

-11

Все клиенты соединились с платой

-12

Теперь давайте попробуем передать из них также строку

-13

Мы видим, что на все пакеты клиенты получили ответы, значит наш сервер клиентов не путает, это хорошо.

Здесь также всё нормально

-14

На дисплее принятые от клиентов строки также отображаются

-15

Попробуем разъединиться

-16

Процесс разъединения прошел корректно

-17

Таким образом, на данном занятии нам удалось создать простенький сервер, работающий по протоколу TCP с несколькими клиентами.

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

Оригинал статьи находится здесь.

<<Предыдущий урок | Следующий урок>>

Исходный код

Недорогие отладочные платы ESP32 можно купить здесь
Недорогие отладочные платы ESP32/ESP32-C3/ESP32-S3 можно купить
здесь
Логический анализатор 16 каналов можно приобрести
здесь
Дисплей LCD 20x4 можно приобрести
тут
Дисплей LCD 16x2 можно приобрести
тут
Переходник I2C to LCD1602 2004 можно приобрести
здесь
Многофункциональный переходник CJMCU FT232H USB к JTAG UART FIFO SPI I2C можно купить
здесь

Видео в Rutube

Программирование МК ESP32. Урок 26. Wi-Fi. STA. TCP Server

Видео в Дзен

Видео в Youtube