Продолжаем работу по программированию микроконтроллера ESP8266 с использованием операционной системы реального времени FREEFTOS. И на данном уроке мы уже начнём работать с протоколом TCP (Transmission Control Protocol). Конечно начнём мы работать с данным протоколом именно с использованием FREERTOS и контроллера ESP8266, а, вообще, мы уже давно с данным протоколом работаем, изучили, можно сказать, его вдоль и поперёк. Мы знаем, что по сравнению с протоколом UDP данный протокол обладает рядом преимуществ — это его надёжность передачи данных, которая обеспечивается тем, что на каждый определённый участок данных требуется подтверждение от принимающей стороны, тем самым обеспечивается гарантированная доставка данных получателю, а также сохранение порядка следования сообщений. Конечно, надёжность эта достаётся не совсем дешевой ценой — нужно корректно создавать соединение с узлом, также корректно разъединяться, обеспечить подтверждение пакетов, следить за порядком следования сегментов и т.д. Но всё это нас не пугает, мы это изучили давным-давно, тем более, что часть данных забот возьмёт на свои плечи библиотека LWIP и её интерфейс SOCKET.
Схема наша также не изменилась и осталась такая же, как и в прошлом уроке
Так как протокол TCP не очень лёгкий, то работу с ним мы также разобьём на несколько уроков и в данном уроке мы создадим простой клиент, который пока только попытается создать соединение с сервером и затем его разорвать.
А проект мы за основу возьмём из урока 25 с именем WIFI_STA_UDP_CLIENT_RX_RTOS и назовём его WIFI_STA_TCP_CLIENT_CONNECT_RTOS.
Откроем наш проект в Eclipse и в файле wifi.c для начала удалим функцию задачи приёма пакетов с сервера recv_task вместе с телом.
Функцию udp_task мы для порядка переименуем в tcp_task.
Переименуем также данную функцию в теле функции wifi_event_handler_cb при создании данной задачи
xTaskCreate(tcp_task, "tcp_task", 4096, NULL, 5, NULL);
В функции tcp_task удалим объявление следующей переменной, так как мы пока не будем пользоваться продвинутой задержкой
portTickType xLastWakeTime;
Объявим переменную структуры очереди, а также сразу проинициализируем её поля
1 2 3 4 |
int sockfd; qData xLCDData; xLCDData.y_pos = 1; xLCDData.str = str1; |
Здесь изменим тип протокола, так как у нас он изменился
if ( (sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0 ) {
os_printf("socket not created\n");
Удалим также параметр здесь, так как он совершенно не нужен
os_printf(«socket binded\n», addr_str);
Также удалим вот этот код
xTaskCreate(recv_task, «recv_task», 1024, (void*)&sockfd, 5, NULL);
xLastWakeTime = xTaskGetTickCount();
for(short i=0;;i++)
{
sendto(sockfd, &i, 2, 0, (struct sockaddr*) &servaddr, sizeof(servaddr));
vTaskDelayUntil( &xLastWakeTime, ( 100 / portTICK_RATE_MS ) );
}
Попытаемся соединиться с сервером, если соединение прошло удачно, то выведем соответствующее сообщение на дисплей
1 2 3 4 5 6 |
servaddr.sin_port = htons(SERVER_PORT); if (connect(sockfd, (struct sockaddr *)&servaddr,sizeof(struct sockaddr_in)) >= 0) { snprintf(str1, sizeof(str1), "Connected"); xQueueSendToBack(xQueue, &xLCDData, 0); } |
Подождём 2 секунды и попытаемся передать строку серверу, после чего подождём ещё 2 секунды
1 2 3 4 5 |
xQueueSendToBack(xQueue, &xLCDData, 0); vTaskDelay( 2000 / portTICK_RATE_MS); snprintf(str1, sizeof(str1), "Hello from ESP!!!\n"); write(sockfd,(void *) str1,strlen(str1)); vTaskDelay( 2000 / portTICK_RATE_MS); |
Так как при выходе из функции мы уже разрываем соединение, то нам уже для этого ничего писать не потребуется.
После попытки разрыва соединения выведем соответствующее сообщение на дисплей
1 2 3 |
close(sockfd); snprintf(str1, sizeof(str1), "Disonnected"); xQueueSendToBack(xQueue, &xLCDData, 0); |
Вот и весь код. Теперь попробуем его проверить.
Для этого мы для начала соберём и прошьём нашу программу, запустим терминальную программу, настроим порт и соединимся по нему с нашей платой, перезагрузим контроллер и посмотрим, какой у нас сетевой адрес
Запустим программу-анализатор трафика Wireshark и отфильтруем поток по данному адресу
Теперь откроем каталог с программой nc (netcat), которой мы также ранее постоянно пользовались для изучения ряда сетевых протоколов, и перейдём в командную строку, в которой введём следующую команду
Номер порта был взят из макроса SERVER_PORT в файле user_config.h.
Перезагрузим контроллер и, если всё правильно, то на дисплее мы сначала увидим следующее сообщение
Затем через 2 секунды в программе netcat мы увидим присланное сообщение
И ещё через 2 секунды на дисплее появится сообщение, сигнализирующее о разрыве соединения
Также весь процесс обмена мы можем наблюдать в Wireshark
Мы видим здесь, что соединение происходит корректно — путём трёхкратного рукопожатия, также обоюдно происходит и его разрыв.
Итак, на данном уроке нам удалось создать простейший TCP-клиент, который способен соединиться и разъединиться с сервером, а также передали на сервер пакет данных.
Всем спасибо за внимание!
Предыдущий урок Программирование МК STM32 Следующий урок
Модуль ESP NodeMCU можно купить здесь: Модуль ESP NodeMCU
Различные модули ЕSP8266 можно приобрести здесь Модули ЕSP8266
Переходник I2C to LCD можно приобрести здесьI2C to LCD1602 2004
Смотреть ВИДЕОУРОК (нажмите на картинку)
Доброго дня! Спасибо за уроки. по ссылочке исходного кода ничего не находится
там ссылка неправильная, замени в ссылке arm на esp8266
спасибо, заменил!
Админ, когда уроки по ESP32 ? Ждем не дождемся 🙂
Здравствуйте Владимир! Стараюсь делать по тексту, т.к. таким образом набивается рука, но текст не без ошибок!
Так, в участке кода в тексте:
xQueueSendToBack(xQueue, &xLCDData, 0);
vTaskDelay( 2000 / portTICK_RATE_MS);
snprintf(str1, sizeof(str1), «Hello from ESP!!!\n»);
write(sockfd,(void *) str1,strlen(str1));
vTaskDelay( 2000 / portTICK_RATE_MS);
а в исходнике, после первой строки закрывающая скобка… Но все равно спасибо, уроки познавательны.
Очень интересные уроки. Не планируете ли вы продолжить их темой вебинтерфейса на базе esp? Это было бы высшей формой практического применения модуля. Пока что всё изложенное в уроках при всей их уникальности и информативности для начинающих годится лишь для самых простых применений далёких от реальных практических задач.