Продолжаем работу по приёму и передаче данных по Wi-Fi.
На данном также мы поработаем с протоколом транспортного уровня UDP, только напишем теперь мы уже сервер. Теперь мы не будем знать ни IP-адрес, не номер порта клиента, который нам будет присылать пакеты с датаграммами, поэтому нам придётся как-то их узнавать, чтобы в ответ посылать пакеты данным клиентам.
Модуль наш также будет работать в режиме станции.
Схема наша не изменилась — отладочная плата, подключенная к ПК по USB
Проект мы создадим на основе проекта прошлого занятия с именем ESPCONN_UDP_CLIENT и назовём его ESPCONN_UDP_SERVER.
Настройку станции в функции user_init мы не трогаем, хотя на первый взгляд может возникнуть сомнение, почему мы по-прежнему запускаем DHCP-клиент, а не сервер, ведь у нас же сервер. Но сервер у нас будет именно UDP, а не DHCP. DHCP-сервер будет подниматься тогда, когда мы будем настраивать наш модуль в режиме точки доступа и мы будем уже не получать адрес IP, а раздавать их другим узлам.
У функции обратного вызова таймера wifi_check_ip тело также не меняется, изменения начинаем вносить в теле функции udp_connect, в которой для начала удалим объявления переменной и массивов, они нам теперь не потребуются
uint32_t ip;
char data[] = «Hello\r\n»;
char udpserverip[15] = {};
Забьём нулями переменную структуры, которая также является полем переменной другой структуры
1 2 |
pConn.proto.udp = &ConnUDP; os_memset(pConn.proto.udp, 0, sizeof(pConn.proto.udp)); |
Вот эти строки также удалим
os_sprintf(udpserverip, «%s», UDPSERVERIP);
ip = ipaddr_addr(udpserverip);
os_memcpy(pConn.proto.udp->remote_ip, &ip, 4);
Локальным портом теперь будет порт сервера, так как у нас и есть сервер
pConn.proto.udp->local_port = UDPSERVERPORT;
А удалённый порт мы не назначаем, он останется нулевым, так как выше мы забили нулями всё поле. Следовательно, данную строку мы также удаляем
pConn.proto.udp->remote_port = UDPSERVERPORT;
Ну и также удалим отправку строки, так как отправлять нам её пока некому, к нам ещё не постучался ни один клиент
espconn_send(&pConn, (uint8_t*)data, strlen(data));
Переходим в функцию обратного вызова приёма пакетов udp_client_udp_recv_cb, в теле которой перед нами теперь встала задача определить адрес и номер порта клиента, приславшего пакет с датаграммой.
Объявим указатель на переменную типа вот такой структуры
1 2 |
uint16_t idx; remot_info *pinfo = NULL; |
Также объявим указатель на переменную известной нам структуры, которая будет уже отвечать за данные клиента и приравняем данный указатель указателю на аргументы из первого параметра функции
1 2 |
remot_info *pinfo = NULL; struct espconn *pEspconn = (struct espconn*)arg; |
Убедимся, что мы приняли именно пакет UDP, и только в этом случае выполним код, находящийся ниже
1 2 3 4 5 6 7 8 9 10 11 |
struct espconn *pEspconn = (struct espconn*)arg; if(pEspconn->type == ESPCONN_UDP) { os_printf("UDP : DATA RECEIVED : LEN = %d\n", length); os_printf("UDP : DATA: "); for(idx=0;idx<length;idx++) { uart_tx_one_char(UART0,pusrdata[idx]); } espconn_send(&pConn, (uint8_t*)"Hello from ESP8266!!!\r\n", 23); } |
В начале тела данного условия с помощью специальной функции библиотеки espconn возьмём информацию о клиенте из переменной структуры, взятой из параметров функции, и поместим её в переменную типа другой структуры. В случае удачного возврата из функции выведем в терминальную программу адрес и номер порта клиента, отправившего нам пакет
1 2 3 4 5 6 7 8 |
if(pEspconn->type == ESPCONN_UDP) { if(espconn_get_connection_info(pEspconn, &pinfo, 0) == 0) { os_printf("Remote IP: %d.%d.%d.%d\r\n", pinfo->remote_ip[0], pinfo->remote_ip[1], \ pinfo->remote_ip[2], pinfo->remote_ip[3]); os_printf("Remote port: %d\r\n", pinfo->remote_port); } |
В противном случае выведем сообщение о том, что определить адрес клиента не удалось
1 2 3 4 5 6 |
os_printf("Remote port: %d\r\n", pinfo->remote_port); } else { os_printf("Cannot get sender IP\r\n"); } |
После вывода в терминальную программу текста принятой датаграммы занесём данные клиента в переменную глобальной структуры
1 2 3 4 |
uart_tx_one_char(UART0,pusrdata[idx]); } os_memcpy(pConn.proto.udp->remote_ip, pinfo->remote_ip, 4); pConn.proto.udp->remote_port = pinfo->remote_port; |
Вот теперь, скорее всего, наш ответный пакет отправится по нужному адресу.
Проверим это, собрав проект и прошив контроллер, а также запустив терминальную программу и соединившись с платой и запустив программу netcat со следующими параметрами
Попробуем отправить сообщение нашему серверу
Сервер благополучно ответил клиенту.
Также мы видим в терминальной программе адрес и номер порта клиента, а также длину и текст принятого от него сообщения
Также можно посмотреть, как происходит обмен, в программе анализа трафика Wireshark
Всё передаётся без ошибок. Отлично!
Таким образом, на данном уроке мы создали простой, но уверенно функционирующий сервер UDP, также мы научились в функции обратного вызова приёма пакета от клиента определять адрес и номер порта отправителя.
Всем спасибо за внимание!
Предыдущий урок Программирование МК ESP8266 Следующий урок
Модуль ESP NodeMCU можно купить здесь: Модуль ESP NodeMCU
Различные модули ЕSP8266 можно приобрести здесь Модули ЕSP8266
Переходник USB to TTL можно приобрести здесь ftdi ft232rl
Многофункциональный переходник JTAG UART FIFO SPI I2C можно приобрести здесь CJMCU FT232H USB к JTAG UART FIFO SPI I2C
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Добавить комментарий