13. UDP Server

Создадим UDP сервер (НЕ ТОЧКУ ДОСТУПА) с фиксированными IP и портом.
(Точка доступа — это DHCP сервер, который сам присуждает IP и порт)

Настроим соединение по UDP протоколу и обменяемся датаграммами.

В файле user_config.h напишем дефайны для точки доступа

#define WiFi_Client_SSID "qwe1" //в SSID обязательно должны быть цифры!!!!
#define WiFi_Client_Password "12345678"

#define UDP_SERVER_PORT 30000

В файле main.c добавляем код:

#include "espconn.h"
//------------------------------------------------------
//User_Variables
//------------------------------------------------------
static os_timer_t os_timer01;
static uint8_t led_state=0;

//for wifi
struct espconn pConn;
esp_udp ConnUDP;
//------------------------------------------------------
//перечисление и переменная с состоянием Wifi
typedef enum{
WIFI_CONNECTING,
WIFI_CONNECTING_ERROR,
WIFI_CONNECTED
} tConnState;

static tConnState ConnState=WIFI_CONNECTING;
//------------------------------------------------------

Далее прописываем прототип функции проверки соединения и callback функции (обозначают окончание приема/передачи по UDP)

static void ICACHE_FLASH_ATTR wifi_check_ip(void *arg); //прототип функции

//------------------------------------------------------
//Functions INTERRUPT WiFi
//------------------------------------------------------
//Вызывается после окончания передачи
void ICACHE_FLASH_ATTR udp_client_Tx(void* arg)
{

}
//------------------------------------------------------
//Вызывается после окончания приема
void ICACHE_FLASH_ATTR udp_client_Rx(void* arg, char* pusrdata, uint16_t length)
{
	//совмещаем структуру с arg (struct	espconn	*pesp_conn=arg;),
	//тем самым получая данные о типе соединения и тд
	struct espconn *pEspconn=(struct espconn*)arg;

	if(pEspconn->type==ESPCONN_UDP) //если UDP
	{
		remot_info *pinfo=NULL;	//указатель на  структуру с данными об отправителе
		if(espconn_get_connection_info(pEspconn, &pinfo, 0)==0)	//копируем информацию об отправителе
		{
			os_printf("Remote IP: ");
			for(uint8_t i=0;i<4;i++) os_printf("%d.",pinfo->remote_ip[i]);
			os_printf("\r\n");
			os_printf("Remote port: %d.\r\n",pinfo->remote_port);
		}
		else
		{
			os_printf("Cannot get sender ID\r\n");
			return;
		}

		//Выводим полученные данные
		os_printf("UDP : DATA RECEIVED : LEN = %d\n", length);
		os_printf("UDP : DATA: ");
		for(uint16_t idx=0;idx<length;idx++) uart_tx_one_char(UART0,pusrdata[idx]); os_printf("\r\n"); os_printf("___________\r\n"); //заносим данные о отправителе в адрес "получателя" os_memcpy(pConn.proto.udp->remote_ip,pinfo->remote_ip,4); //копируем ip
		pConn.proto.udp->remote_port=pinfo->remote_port;

		espconn_send(&pConn, (uint8_t*)"Hello from ESP8266!!!\r\n", 23);
	}
	else
	{
		os_printf("It is not UDP ask\r\n");
	}
}

Примечание: Использовать буфер в виде структуры remot_info *pinfo необходимо, по крайней мере я не смог получить НЕНУЛЕВЫЕ данные из структуры pEspconn.

Далее необходимо написать функцию создания UDP передачи

//------------------------------------------------------
//Создание UDP передачи
//------------------------------------------------------
static void ICACHE_FLASH_ATTR udp_connect()
{
	os_timer_disarm(&os_timer01);
	pConn.proto.udp = &ConnUDP;
	os_memset(pConn.proto.udp,0,sizeof(pConn.proto.udp)); //обнуляем поле UDP (ip и порты)
	pConn.type = ESPCONN_UDP;		//устанавливаем тип
	pConn.state = ESPCONN_NONE;		//тукущее состояние

	//Вводим порт отправителя
	pConn.proto.udp->local_port = UDP_SERVER_PORT; 	//создаем заданный порт для ESP8266

	//создаем "соединение" по факту - открываем канал связи.
	os_printf("UDP espconn create...\r\n");
	espconn_create(&pConn);							//создаем UDP передачу
	espconn_regist_sentcb(&pConn, udp_client_Tx);	//указываем callback функцию для передачи 	(вызывается после отправки)
	espconn_regist_recvcb(&pConn, udp_client_Rx);	//указываем callback функцию для приема		(вызывается после приема)

	//Запускаем таймер для проверки что соединение все еще есть
	os_timer_setfn(&os_timer01, (os_timer_func_t *)wifi_check_ip, NULL);
	os_timer_arm(&os_timer01, 1000, 0);

}

Допишем функцию, отвечающую за wifi соединение (см. Wi-Fi STA (станция/клиент))

//------------------------------------------------------
//Functions INTERRUPT
//------------------------------------------------------
static void ICACHE_FLASH_ATTR wifi_check_ip(void *arg)
{
	struct ip_info IpConfig;	//структура, в которую "складируются" данные о текущем соединении
	os_timer_disarm(&os_timer01); 	//деинициализировать (обнулить) структуру / отключаем таймер

	uint8_t WiFi_status=wifi_station_get_connect_status(); //проверяем статус соединения

	if(WiFi_status==STATION_GOT_IP)
	{
		wifi_get_ip_info(STATION_IF, &IpConfig); //получаем ip и другую информацию о esp8266

		if (IpConfig.ip.addr!=0) //если получили IP, то меняем статус и выходим
		{
			if(ConnState!=WIFI_CONNECTED) //если есть соединение, то выходим в функцию udp_connect()
			{
				ConnState=WIFI_CONNECTED;
				os_printf("WiFi connected\r\n");
				udp_connect();
				return;
			}
			else {led_state=(led_state==0) ? 1:0;	GPIO_OUTPUT_SET(2,led_state);	} //мигаем светодиодом
		}

	}
	else
	{
		espconn_delete(&pConn); //разрываем соединение с хостом (точнее удаляем передачу)

		switch (WiFi_status)
		{
			case STATION_WRONG_PASSWORD:
				ConnState=WIFI_CONNECTING_ERROR;
				os_printf("STATION_WRONG_PASSWORD\r\n");
			break;

			case STATION_NO_AP_FOUND:
				ConnState=WIFI_CONNECTING_ERROR;
				os_printf("STATION_NO_AP_FOUND\r\n");
			break;

			case STATION_CONNECT_FAIL:
				ConnState=WIFI_CONNECTING_ERROR;
				os_printf("STATION_CONNECT_FAIL\r\n");
			break;

			default:
				ConnState=WIFI_CONNECTING;
				os_printf("STATION_IDLE\r\n");
			break;

		}
	}

	os_timer_setfn(&os_timer01, (os_timer_func_t*)wifi_check_ip, NULL); //указываем структура и функцию обратного вызова
	os_timer_arm(&os_timer01, 500, 0); 	//инициализировать (обнулить) структуру
}

Последний шаг — напишем функцию main

//------------------------------------------------------
void ICACHE_FLASH_ATTR user_init()
{
	uart_init(BIT_RATE_115200, BIT_RATE_115200);
	//-----
	gpio_init();
	PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
	Set_GPIOs_As_Output(BIT2, 0);

	//-----
	ets_delay_ms(100);
	os_printf("\r\n");


	//
	//WiFi UDP Client
	//
	struct station_config stationConf;  //структура для настроек wifi

	wifi_set_opmode(STATION_MODE);  //режим станции (настройка м.б. изменена "на лету")
	wifi_station_disconnect();	//разрываем соединение
	wifi_station_dhcpc_stop();      //Остановим DHCP клиент

	if(wifi_station_get_config(&stationConf))
	  {
	    os_memset(stationConf.ssid, 0, sizeof(stationConf.ssid));
	    os_memset(stationConf.password, 0, sizeof(stationConf.password));
	    os_sprintf(stationConf.ssid, "%s", WiFi_Client_SSID);
	    os_sprintf(stationConf.password, "%s", WiFi_Client_Password);
	    if(!wifi_station_set_config(&stationConf))
	    {
	      os_printf("Not set station config!\r\n");
	    }
	  }


	wifi_set_sleep_type(NONE_SLEEP_T); 	//отключаем пониженное энергопотребление
	wifi_station_connect(); 		//присоединяемся к wifi
	wifi_station_dhcpc_start(); 		//запускаем DHCP клиент

	if(wifi_get_phy_mode() != PHY_MODE_11N) wifi_set_phy_mode(PHY_MODE_11N); 	//Включаем режим 802.11n
	if(wifi_station_get_auto_connect() ==0) wifi_station_set_auto_connect(1);	//включаем автоконнект

	//используем программный таймер
	os_timer_disarm(&os_timer01); 										//деинициализировать (обнулить) структуру
	os_timer_setfn(&os_timer01, (os_timer_func_t*)wifi_check_ip, NULL); //указываем структура и функцию обратного вызова
	os_timer_arm(&os_timer01, 1000, 0); 								//0-однократно, 1-циклический

}

Принцип работы описанной выше программы:

Большая часть программы аналогична работе UDP Client:

В main настраиваем wifi и включаем программный таймер, через 1с попадаем в прерывание по таймеру, проверяем статус соединения и выставляем новый таймер с тиком 500мс (для проверки соединения)

>>если нет соединения, ставим флаг ошибки и выводим в uart ошибку

>>если соединение есть:

  • при первичном запуске (IpConfig.ip.addr!=0 и ConnState!=WIFI_CONNECTED), т.е. ip получили, но статус не обновили — изменяем статус «ConnState=WIFI_CONNECTED;» и вызываем функцию udp_connect();
  • при повторных запусках мигаем светодиодом

Функция udp_connect() — настраиваем порт отправителя для UDP (30000 порт).

При получении сбщ. от сервера вызывается функция «udp_client_Rx», ВНУТРИ НЕЕ получаются данные об отправителе (ip и порт) и в ответ посылается сообщение «Hello from ESP8266!!!».

Для эмуляции работы клиента:
>> Запустить netcat
>> прописать: nc -u ip 30000 (этим откроем соединение)
где ip esp8266 берется из терминальной программы
>>отправить данные в esp8266 (они также отобразятся в терминальной программе)
>>получить ответ от esp8266 (Hello from ESP8266!!!)

Примечание: команды терминальной программы в win7:
Узнать свой ip: win+R ввести cmd и прописать ipconfig
Перейти в папку с netcat: win+R ввести cmd и прописать cd «путь до netcat»

Запускаем netcat в командной строке из папки с программой:
прописываем nc -u -l -p 30000
этим мы открыли 30000 порт и слушаем его, можем что-то в него отправить (вписываем данные +enter)

 

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

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