ESP-12S学习(7)--ESP8266TCP和UDP的客户端和服务端

编程知识 更新时间:2023-04-25 08:00:23
一、前言

文章《ESP-12S学习⑤–Get拿天气数据》采用的就是TCP的连接方式,访问的是服务器,所以ESP8266做的是客户端

ESP8266可以扮演四种角色,分别是TCP客户端TCP服务端UDP客户端UDP服务端

TCP客户端

相当于个人终端,比如手机电脑,这个时候对ESP8266的配置应该是配置为本地,手机开启TCP服务端,提供远端端口和ip地址

ESP8266与手机的通讯流程:

  1. ESP8266连接WiFi
  2. 定时器每隔500ms检测连接WiFi状态是不是成功
  3. 如果连接成功,初始化espconn结构体(连接类型、远近ip、远近端口),注册连接成功,重新连接等操作的回调函数
  4. 在成功连接的回调函数里面注册发送成功,接收成功,断开连接等操作的回调函数,并且调用espconn_send向服务端发送数据

只能在局域网内通信,手机和ESP8266连接同一个路由器

/******************************************/
os_timer_t	ledtimer;
struct station_config	wifi_config;
struct espconn pespconn;
const char remote_ip[4] = {192, 168, 1, 101};	// 手机的ip地址

/******************************************/

/**
 * @name	Sent_Data[发送、数据]
 */
void Sent_Data()
{
	os_printf("connect succeed!!!\n");
	espconn_regist_sentcb(&pespconn, Sent_Succeed);	// 注册发送成功
	espconn_regist_recvcb(&pespconn, Receive_Succeed);	// 注册接收成功
	espconn_regist_disconcb(&pespconn, Release_Succeed);	// 注册断开
	espconn_send(&pespconn, "Hello!", strlen("Hello!"));	// 发送
}

/**
 * @name	Tcp_Disconect[TCP连接失败]
 */
void Tcp_Disconect(void *arg, sint8 err)
{
	os_printf("Connect fail!!!\n");
	os_printf("Reconnect!!!\n");
	espconn_connect(&pespconn);
}

/**
 * @name	Sent_Succeed[数据发送成功]
 */
void Sent_Succeed()
{
	os_printf("Send succeed!!!\n");
}

/**
 * @name	Receive_Succeed[接收成功]
 */
void Receive_Succeed(void *arg, char *pdata, unsigned short len)
{
	os_printf("Receive succeed!!!\n");
	os_printf("Data:%s\n", pdata);		// 打印接收数据
}

/**
 * @name	Release_Succeed[TCP连接释放]
 */
void Release_Succeed()
{
	os_printf("Release succeed!!!\n");
}

/**
 * @name	Espconn_Init[连接结构体初始化]
 */
void ICACHE_FLASH_ATTR Espconn_Init(ip_addr_t  *remote_ip, int remote_port)
{
	struct ip_info ststion_info;
	wifi_get_ip_info(0x00, &ststion_info);

	os_printf("init espconn struct!!!\n");
	/*配置连接结构体*/
	pespconn.type = ESPCONN_TCP;
	pespconn.state = ESPCONN_NONE;

	pespconn.proto.tcp = (esp_tcp *) os_zalloc(sizeof(esp_tcp));	// 分配内存填充0
	os_memcpy(pespconn.proto.tcp->local_ip, &ststion_info.ip, 4);
	os_memcpy(pespconn.proto.tcp->remote_ip, remote_ip, 4);
	pespconn.proto.tcp->local_port = espconn_port();
	pespconn.proto.tcp->remote_port = remote_port;

	/*注册*/
	espconn_regist_connectcb(&pespconn, Sent_Data);
	espconn_regist_reconcb(&pespconn, Tcp_Disconect);

	/*连接服务器*/
	os_printf("start connect!!!\n");
	espconn_connect(&pespconn);
}


void ICACHE_FLASH_ATTR Check(void)
{
	ip_addr_t addr;
	struct espconn pespconn;
    os_printf("Check!\n");
    /*查询wifi的状态*/
    if (wifi_station_get_connect_status() == STATION_GOT_IP)
    {
    	os_timer_disarm(&ledtimer);
    	if (wifi_station_get_connect_status() == STATION_GOT_IP)	// 再次确认
    	{
    		os_printf("succeed!\n");
    		os_printf("wifi_name:MST+QR\n");
    		Espconn_Init((struct ip_addr *)remote_ip, 10086);	//给服务器端的ip和端口号,也就是手机开启的
    	}
    }
    else
    {
    	os_printf("fail!\n");
    }
}


/*
 * @name:	user_init[用户程序入口]
 */
void ICACHE_FLASH_ATTR
user_init(void)
{
	system_timer_reinit();
	uart_init(74880, 74880);	//设置串口0和串口1的波特率
    os_printf("SDK version:%s\n", system_get_sdk_version());
    os_printf("Hello8266!\n");

    wifi_set_sleep_type(NONE_SLEEP_T);	// 关闭睡眠

    // 设置wifi的工作模式
    wifi_set_opmode(0x01);	// Station模式

    os_printf("Config!\n");
    /*设置wifi密码名字*/
    os_strcpy(wifi_config.ssid, "MST+QR");
    os_strcpy(wifi_config.password, "123123123");
    wifi_station_set_config(&wifi_config);
    wifi_station_connect();

	/*软件定时器*/
	os_timer_disarm(&ledtimer);									// 关闭定时器
	os_timer_setfn(&ledtimer, (os_timer_func_t *)Check, (void*)0);			// 设置定时器的回调函数
	os_timer_arm_us(&ledtimer, 500000, 1);						// 打开定时器,500ms,重复

}


配合上一节的SmartConfig使用局域网通信


TCP服务端

可以理解为一个网关,发射热点

①:关于AP的配置

void ICACHE_FLASH_ATTR WiFi_Init(void)
{
    // 设置wifi的工作模式
    wifi_set_opmode(0x02);	// AP模式
    os_printf("APConfig!\n");

    /*AP信息设置*/
    ap_config.ssid_len = 12;					// 根据自己的wifi名字长度来设置
    os_strcpy(ap_config.ssid, "TP-LINK-8266");	// wifi名字
    os_strcpy(ap_config.password, "82668266");	// wifi的密码
    ap_config.channel = 1;						// wifi通道1~13
    ap_config.max_connection = 2;				// 最大的连接数2
    ap_config.beacon_interval = 100;			// [信标间隔(认为是心跳就好了)](https://wwwblogs/zhanglinf/p/4584499.html)
    ap_config.authmode = AUTH_WPA2_PSK;         // 设置加密模式
    ap_config.ssid_hidden = 0;					// 不隐藏wifi

    wifi_softap_set_config(&ap_config);		// 设置并保存AP到flash
}

②:关于espconn的配置
ESP8266TCP Server,不需要知道连接它的设备的ip和端口,但是需要提供ip和端口给需要连接的设备

void ICACHE_FLASH_ATTR Espconn_Init(int local_port)
{
	os_printf("init espconn struct!!!\n");
	/*配置连接结构体*/
	pespconn.type = ESPCONN_TCP;
	pespconn.state = ESPCONN_NONE;

	pespconn.proto.tcp = (esp_tcp *) os_zalloc(sizeof(esp_tcp));	// 分配内存填充0
	pespconn.proto.tcp->local_port = local_port;	// 设置一个端口(提供端口)

	/*注册*/
	espconn_regist_connectcb(&pespconn, Sent_Data);
	espconn_regist_reconcb(&pespconn, Tcp_Disconect);

	/*监听TCP服务器*/
	os_printf("start connect!!!\n");
	espconn_accept(&pespconn);
	espconn_regist_time(&pespconn, 120, 0);
}

ip地址自己会打印出来

ESP8266与手机的通讯流程:

  • ESP8266发射热点,等待手机接入
/******************************************/
struct espconn user_tcp_espconn;

/******************************************/

/**
 * @name	Tcp_Disconect[TCP连接失败]
 */
void Tcp_Disconect(void *arg, sint8 err)
{
	struct espconn *pespconn = arg;
	os_printf("Connect fail!!!\n");
	os_printf("Reconnect!!!\n");
	espconn_connect(pespconn);
}

/**
 * @name	Sent_Succeed[数据发送成功]
 */
void Sent_Succeed(void *arg)
{
	os_printf("Send succeed!!!\n");
}

/**
 * @name	Receive_Succeed[接收成功]
 */
void Receive_Succeed(void *arg, char *pdata, unsigned short len)
{
	os_printf("Receive succeed!!!\n");
	os_printf("Data:%s\n", pdata);		// 打印接收数据
}

/**
 * @name	Release_Succeed[TCP连接释放]
 */
void Release_Succeed()
{
	os_printf("Release succeed!!!\n");
}

/**
 * @name	Sent_Data[发送数据]
 */
void Sent_Data(void *arg)
{
	os_printf("connect succeed!!!\n");
	struct espconn *pespconn = arg;
	espconn_regist_sentcb(pespconn, Sent_Succeed);	// 注册发送成功
	espconn_regist_recvcb(pespconn, Receive_Succeed);	// 注册接收成功
	espconn_regist_disconcb(pespconn, Release_Succeed);	// 注册断开
	espconn_send(pespconn, "Hello!", strlen("Hello!"));	// 发送
}

/**
 * @name	Espconn_Init[连接结构体初始化]
 */
void ICACHE_FLASH_ATTR Espconn_Init(int local_port)
{
	struct dhcps_lease please;
	os_printf("init espconn struct!!!\n");
	/*配置连接结构体*/
	user_tcp_espconn.type = ESPCONN_TCP;

	user_tcp_espconn.proto.tcp = (esp_tcp *) os_zalloc(sizeof(esp_tcp));	// 分配内存填充0
	user_tcp_espconn.proto.tcp->local_port = local_port;

	/*注册*/
	espconn_regist_connectcb(&user_tcp_espconn, Sent_Data);
	espconn_regist_reconcb(&user_tcp_espconn, Tcp_Disconect);

	/*监听TCP服务器*/
	os_printf("start connect!!!\n");
	espconn_accept(&user_tcp_espconn);
	espconn_regist_time(&user_tcp_espconn, 120, 0);
}


void ICACHE_FLASH_ATTR WiFi_Init(void)
{
	struct ip_info	APip_info;
	struct softap_config ap_config;
    // 设置wifi的工作模式
    wifi_set_opmode(0x02);	// AP模式

    os_printf("APConfig!\n");

    /*AP信息设置*/
    ap_config.ssid_len = 12;
    os_strcpy(ap_config.ssid, "TP-LINK-8266");
    os_strcpy(ap_config.password, "82668266");
    ap_config.channel = 1;
    ap_config.max_connection = 2;
    ap_config.beacon_interval = 100;
    ap_config.authmode = AUTH_WPA2_PSK;         //设置加密模式
    ap_config.ssid_hidden = 0;

    wifi_softap_set_config(&ap_config);		// 设置并保存AP

}


/*
 * @name:	user_init[用户程序入口]
 */
void ICACHE_FLASH_ATTR
user_init(void)
{
	system_timer_reinit();
	uart_init(74880, 74880);	//设置串口0和串口1的波特率
    os_printf("SDK version:%s\n", system_get_sdk_version());
    os_printf("Hello8266!\n");

    os_printf("**************************************************\n");
    WiFi_Init();
    Espconn_Init(10086);

}


UDP客户端

UDP客户端与UDP服务端通信是不需要建立连接的,虽然是不建立连接,但是还是需要知道目的地址和端口号,UDP可以发广播,发广播使用的地址是255.255.255.255

ESP8266有两种方式和手机的UDP Server通信,一个是通过广播,二个是通过手机的ip地址,有什么区别呢

* 如果手机是连接着路由器的,那么ESP8266就没有办法通过广播地址和手机UDP Server通信,这是不允许的

ESP8266是可以通过广播跟连接着路由器的手机通信的2020.6.22补
  • 如果ESP8266是直接连接手机的,那么ESP8266就可以通过广播地址和手机ip与手机UDP Server通信
os_timer_t	ledtimer;

/******************************************/
struct station_config	wifi_config;
struct espconn pespconn;
char remote_ip[4] = {192, 168, 1, 102};

/******************************************/

/**
 * @name	Sent_Succeed[数据发送成功]
 */
void Sent_Succeed(void *arg)
{
	os_printf("Send succeed!!!\n");
}

/**
 * @name	Receive_Succeed[接收成功]
 */
void Receive_Succeed(void *arg, char *pdata, unsigned short len)
{
	os_printf("Receive succeed!!!\n");
	os_printf("Data:%s\n", pdata);		// 打印接收数据
	espconn_send((struct espconn *)arg, "Hello", strlen("Hello"));
}

/**
 * @name	Espconn_Init[连接结构体初始化]
 */
void ICACHE_FLASH_ATTR Espconn_Init(ip_addr_t  *remote_ip, int local_port, int remote_port)
{
	os_printf("init espconn struct!!!\n");
//	wifi_set_broadcast_if(0x01);
	/*配置连接结构体*/
	pespconn.type = ESPCONN_UDP;
	pespconn.state = ESPCONN_NONE;

	pespconn.proto.udp = (esp_udp *) os_zalloc(sizeof(esp_udp));	// 分配内存填充0
	os_memcpy(pespconn.proto.udp->remote_ip, remote_ip, 4);			// 目的地址广播
	pespconn.proto.udp->local_port = local_port;
	pespconn.proto.udp->remote_port = remote_port;

	/*注册*/
	espconn_regist_recvcb(&pespconn, Receive_Succeed);	// 注册接收成功
	espconn_regist_sentcb(&pespconn, Sent_Succeed);		// 注册发送成功

	/*连接服务器*/
	os_printf("UDP create!!!\n");
	espconn_create(&pespconn);
	espconn_send(&pespconn, "Hello", strlen("Hello"));
}


void ICACHE_FLASH_ATTR Check(void)
{
	ip_addr_t addr;
	struct espconn pespconn;
    os_printf("Check!\n");
    /*查询wifi的状态*/
    if (wifi_station_get_connect_status() == STATION_GOT_IP)
    {
    	os_timer_disarm(&ledtimer);
    	if (wifi_station_get_connect_status() == STATION_GOT_IP)	// 再次确认
    	{
    		os_printf("succeed!\n");
    		os_printf("wifi_name:MST+QR\n");
    		Espconn_Init((struct ip_addr *)remote_ip, 10000, 10086);	//给服务器端的ip和端口号,也就是手机开启的
    	}
    }
    else
    {
    	os_printf("fail!\n");
    }
}


/*
 * @name:	user_init[用户程序入口]
 */
void ICACHE_FLASH_ATTR
user_init(void)
{
	system_timer_reinit();
	uart_init(74880, 74880);	//设置串口0和串口1的波特率
    os_printf("SDK version:%s\n", system_get_sdk_version());
    os_printf("Hello8266!\n");

    wifi_set_sleep_type(NONE_SLEEP_T);	// 关闭睡眠

    // 设置wifi的工作模式
    wifi_set_opmode(0x01);	// Station模式

    os_printf("Config!\n");
    /*设置wifi密码名字*/
    os_strcpy(wifi_config.ssid, "MST+QR");
    os_strcpy(wifi_config.password, "123123123");
    wifi_station_set_config(&wifi_config);
    wifi_station_connect();

	/*软件定时器*/
	os_timer_disarm(&ledtimer);										// 关闭定时器
	os_timer_setfn(&ledtimer, (os_timer_func_t *)Check, (void*)0);	// 设置定时器的回调函数
	os_timer_arm_us(&ledtimer, 500000, 1);							// 打开定时器,500ms,重复

}

不知道为什么,手机端是可以接收到ESP8266信息的,但是手机没有办法发送信息给ESP8266


UDP服务端

某种意义上无法明确区分UDP服务器端和UDP客户端

配置wifi热点:
还是和上面的TCP服务器模式一致

void ICACHE_FLASH_ATTR WiFi_Init(void)
{
	struct ip_info	APip_info;
    // 设置wifi的工作模式
    wifi_set_opmode(0x02);	// AP模式

    os_printf("APConfig!\n");

    /*AP信息设置*/
    ap_config.ssid_len = 12;
    os_strcpy(ap_config.ssid, "TP-LINK-8266");
    os_strcpy(ap_config.password, "82668266");
    ap_config.channel = 1;
    ap_config.max_connection = 2;
    ap_config.beacon_interval = 100;
    ap_config.authmode = AUTH_WPA2_PSK;         //设置加密模式
    ap_config.ssid_hidden = 0;

    wifi_softap_set_config(&ap_config);		// 设置并保存AP

}

初始化UDP连接结构体:

设置两个端口,注册接收回调函数,还是用espconn_regist_recvcb()注册

void ICACHE_FLASH_ATTR Espconn_Init(int local_port, int remote_port, struct ip_addr *remote_ip)
{
	os_printf("init espconn struct!!!\n");
	/*配置连接结构体*/
	pespconn.type = ESPCONN_UDP;
	pespconn.state = ESPCONN_NONE;

	pespconn.proto.udp = (esp_udp *) os_zalloc(sizeof(esp_udp));	// 分配内存填充0
	pespconn.proto.udp->remote_port = remote_port;
	pespconn.proto.udp->local_port = local_port;
//	memcpy(pespconn.proto.udp->remote_ip, remote_ip, 4);

	espconn_regist_recvcb(&pespconn, Receive_Function);  //接收

	os_printf("UDP start !!!\n");

	espconn_create(&pespconn);	// 创建UDP连接
}

接收回调函数:

void Receive_Function(void *arg, char *pdata, unsigned short len)
{
	sint16 ret;
	os_printf("Receive:%s\r\n", pdata);
	espconn_sent((struct espconn *) arg, "ok", strlen("ok"));
	ret = espconn_sendto((struct espconn *) arg, "Hello", strlen("Hello"));
	os_printf("ret:%d\n", ret);
}

同样的,手机端总是接收不到ESP8266发过来的信息,但是手机端是可以发送信息的 ,手机端需要填写ESP8266端的ip地址和端口号,ip地址可以写确定的ESP8266地址,也可以写广播地址255.255.255.255


那个单方向传输的问题先放着吧,不知道怎么回事


2020.6.4

TCP服务器的总代码修改了,struct espconn user_tcp_espconn;这里面的user_tcp_espconn最好和后面的注册函数的区分开来,注册函数是有传参的,但是我写的是没有传参的,导致没有办法接收到信息,其他的几个也应该是这样,但是我这里没有修改

2020.6.19

更多推荐

ESP-12S学习(7)--ESP8266TCP和UDP的客户端和服务端

本文发布于:2023-04-19 03:35:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/3dcb00dda0fbd8842caab43d2ffff42f.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:服务端   客户端   ESP   UDP   ESP8266TCP

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!

  • 87107文章数
  • 19107阅读数
  • 0评论数