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



Продолжаем работу с протоколом 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, в котором отфильтруемся по адресу нашей платы, который мы увидим в терминале при загрузке программы в контроллере.

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

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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

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

 

 

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

 

 

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

 

 

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

 

 

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

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

 

Данная статья в Дзен.

 

 

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

 

Исходный код

 

 

Недорогие отладочные платы ESP32 можно купить здесь:

На AliExpress Недорогие отладочные платы ESP32

На Яндекс.Маркет Недорогие отладочные платы ESP32

Логический анализатор 16 каналов можно приобрести (AliExpress) здесь

Дисплей LCD 20×4 можно приобрести здесь (AliExpress) Дисплей LCD 20×4

Дисплей LCD 16×2 (AliExpress)

Переходник I2C to LCD можно приобрести здесь (AliExpress) I2C to LCD1602 2004

Дисплей символьный LCD 1602 с впаянным переходником на шину I2C (Яндекс.Маркет)

 

 

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

ESP32 Wi-Fi. STA. TCP Server

 

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

ESP32 Wi-Fi. STA. TCP Server

Один комментарий на “ESP32 Урок 26. Wi-Fi. STA. TCP Server

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

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

*