Продолжаем работу по приёму и передаче данных по 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
Многофункциональный переходник CJMCU FT232H USB к JTAG UART FIFO SPI I2C можно приобрести здесь ftdi ft232rl
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК (нажмите на картинку)
Добавить комментарий