1、介绍
ESP8266工作在AP模式,创建一个WiFi局域网,笔记本连接此WiFi;
ESP8266,IP:192.168.4.1,端口:8266;
网络调试助手,IP:192.168.4.2,端口:6666;
使用UDP连接,有数据直接发,不需要建立连接;
客户端/服务器模式,服务器先运行,客户端要知道服务器的IP和端口,然后就可以发送UDP数据报,服务器接收UDP数据报,这时服务器也知道客户端的IP和端口了,可以响应客户端,即发送UDP数据报;
下面介绍ESP8266做为UDP连接的服务器,服务器和客户端其实并没多大区别,只不过作为服务器一开始你只要把自己端口设置好,等着就行了;作为客户端不仅要设置自己的端口,也要知道对方的IP和端口,仅此而已,文末有对比;
2、ESP8266作为UDP连接的服务器
2.1、设置ESP8266为AP模式,两步
- 设置ESP8266工作模式为AP;
- 配置AP参数:SSID、password、加密方式、是否隐藏WiFi名...
#define ESP8266_AP_SSID "LOVEYOU"
#define ESP8266_AP_PASS "Be Happy"
//初始化AP模式,设置WiFi名和密码
void ICACHE_FLASH_ATTR AP_mode_init()
{
struct softap_config ap_config;
wifi_set_opmode(0x02); // 设置为AP模式,并保存到Flash
os_memset(&ap_config, 0, sizeof(struct softap_config)); // AP参数结构体 = 0
os_strcpy(ap_config.ssid,ESP8266_AP_SSID); // 设置SSID(将字符串复制到ssid数组)
os_strcpy(ap_config.password,ESP8266_AP_PASS); // 设置密码(将字符串复制到password数组)
ap_config.ssid_len=os_strlen(ESP8266_AP_SSID); // 设置ssid长度(和SSID的长度一致)
ap_config.channel=1; // 通道号1~13
ap_config.authmode=AUTH_WPA2_PSK; // 设置加密模式
ap_config.ssid_hidden=0; // 不隐藏SSID
ap_config.max_connection=4; // 最大连接数
ap_config.beacon_interval=100; // 信标间隔时槽100~60000 ms
wifi_softap_set_config(&ap_config); // 设置soft-AP,并保存到Flash
}
2.2、初始化UDP通信_espconn_create()
所谓初始化UDP通信,就是初始化一个全局的struct espconn变量,里面的信息有:
- 选择TCP或UDP通信;
- 连接状态;
- TCP/UDP协议下,本机和远端的IP及port;
- 发送、接收数据报的回调函数;
- 连接数;
struct espconn {
/** type of the espconn (TCP, UDP) */
enum espconn_type type;
/** current state of the espconn */
enum espconn_state state;
union {
esp_tcp *tcp;
esp_udp *udp;
} proto;
/** A callback function that is informed about events for this espconn */
espconn_recv_callback recv_callback;
espconn_sent_callback sent_callback;
uint8 link_cnt;
void *reverse;
};
//此结构体,选择TCP/UDP,端口等信息;
struct espconn ST_NetCon; //必须定义为全局变量,内核将会使用此变量(内存)
void UDP_send_callback();
void UDP_recv_callback(void * arg, char * pdata, unsigned short len);
//初始化UDP通信,端口8266;
void ICACHE_FLASH_ATTR UDP_port8266_init()
{
ST_NetCon.type = ESPCONN_UDP; //通信协议:UDP
ST_NetCon.proto.udp = (esp_udp *)os_zalloc(sizeof(esp_udp)); // 申请内存
//ST_NetCon.proto.udp->local_port = espconn_port(); //获取8266可用端口,这是动态的端口
ST_NetCon.proto.udp->local_port = 8266 ; // 设置本地端口
espconn_regist_sentcb(&ST_NetCon,UDP_send_callback); // 注册网络数据发送成功的回调函数
espconn_regist_recvcb(&ST_NetCon,UDP_recv_callback); // 注册网络数据接收成功的回调函数
espconn_create(&ST_NetCon); //初始化UDP通信
}
void UDP_send_callback()
{
os_printf("UDP seriver send over\n");
}
//作为服务器收到客户端UDP报文的回调函数(参数列表必须是这些)
//arg:网络传输结构体espconn指针,保存客户端的IP和端口
//pdata:报文的数据指针
//len:报文的长度
void UDP_recv_callback(void * arg, char * pdata, unsigned short len)
{
struct espconn * T_arg = arg; // 缓存网络连接结构体指针
remot_info * P_port_info = NULL; // 远端连接信息结构体指针
u8 buffer[20];
os_printf("\nESP8266_Receive_Data = %s\n",pdata); // 串口打印接收到的数据
// 获取远端信息【UDP通信是无连接的,向远端主机回应时需获取对方的IP/端口信息】
if(espconn_get_connection_info(T_arg, &P_port_info, 0)==ESPCONN_OK) // 获取远端信息
{
T_arg->proto.udp->remote_port = P_port_info->remote_port; // 获取对方端口号
T_arg->proto.udp->remote_ip[0] = P_port_info->remote_ip[0]; // 获取对方IP地址
T_arg->proto.udp->remote_ip[1] = P_port_info->remote_ip[1];
T_arg->proto.udp->remote_ip[2] = P_port_info->remote_ip[2];
T_arg->proto.udp->remote_ip[3] = P_port_info->remote_ip[3];
//os_memcpy(T_arg->proto.udp->remote_ip,P_port_info->remote_ip,4); // 内存拷贝
}
//显示远端主机IP地址
os_sprintf(buffer,"IP:%d.%d.%d.%d",T_arg->proto.udp->remote_ip[0],T_arg->proto.udp->remote_ip[1],T_arg->proto.udp->remote_ip[2],T_arg->proto.udp->remote_ip[3]);
oled_show_string(0,2,buffer,FONT_8x16);
// 向对方发送应答
espconn_send(T_arg,"ESP8266_WIFI_Recv_OK",os_strlen("ESP8266_WIFI_Recv_OK"));
}
2.3、辅助代码、以及user_init
os_timer_t timer_ap_check; //检测AP模式设置完成,已能获取本机IP定时器
//检测AP模式设置完成,已能获取本机IP定时器
void timer_ap_check_callback()
{
struct ip_info IP_info;
u8 ip_dec[4];
u8 buffer[20];
// 查询并打印ESP8266的工作模式
switch(wifi_get_opmode()) // 输出工作模式
{
case 0x01: os_printf("\nESP8266_Mode = Station\n"); break;
case 0x02: os_printf("\nESP8266_Mode = SoftAP\n"); break;
case 0x03: os_printf("\nESP8266_Mode = Station+SoftAP\n"); break;
}
//AP模式下,默认开启DHCP,默认IP:192.168.4.1
//-----------------------------------------------------------------------------------
wifi_get_ip_info(SOFTAP_IF,&IP_info); // 参数2:IP信息结构体指针
if(IP_info.ip.addr != 0){ //已获取到本机IP地址,初始化UDP
//32位二进制IP地址,将它转换为点分十进制的形式
ip_dec[0] = IP_info.ip.addr;
ip_dec[1] = IP_info.ip.addr>>8;
ip_dec[2] = IP_info.ip.addr>>16;
ip_dec[3] = IP_info.ip.addr>>24;
// 打印ESP8266的IP地址
os_printf("ESP8266_IP = %d.%d.%d.%d\n",ip_dec[0],ip_dec[1],ip_dec[2],ip_dec[3]);
os_sprintf(buffer,"IP:%d.%d.%d.%d",ip_dec[0],ip_dec[1],ip_dec[2],ip_dec[3]);
oled_show_string(0,0,buffer,FONT_8x16);
//初始化UDP
UDP_port8266_init();
//关闭定时器
os_timer_disarm(&timer_ap_check);
}else{
os_printf("waiting a minute...\n");
}
// 查询并打印接入此WIFI的设备数量
//os_printf("Number of devices connected to this WIFI = %d\n",wifi_softap_get_station_num());
}
void ICACHE_FLASH_ATTR timer_init()
{
os_timer_disarm(&timer_ap_check);
os_timer_setfn(&timer_ap_check,timer_ap_check_callback,NULL);
os_timer_arm(&timer_ap_check,1000,1);
}
//*******************用户初始化函数**********************
void ICACHE_FLASH_ATTR user_init(void)
{
u8 i;
uart_init(115200,115200);
my_delay_ms(1000); //等待串口稳定,避免起始数据错误
system_soft_wdt_feed(); //喂狗,防止复位
AP_mode_init(); //设置AP模式
oled_init();
timer_init();
}
关于AP的头文件别忘了...
#include "ip_addr.h" // 被"espconn.h"使用,在"espconn.h"开头#include"ip_addr.h"或#include"ip_addr.h"放在"espconn.h"之前
#include "espconn.h" // TCP/UDP接口
3、下载、验证
当笔记本接入此WiFi时,提示station:f4:96:34:f5:bd:47加入此AP,这一串是笔记本的MAC地址;
ESP8266与笔记本之间的链路层传输,就要用到MAC地址;
4、作为服务器、客户端的不同
作为服务器:
- proto.udp 只提供本地端口local port;
- 等待接收UDP数据报,进入接收回调函数;
//初始化UDP通信,端口8266;
void ICACHE_FLASH_ATTR UDP_port8266_init()
{
ST_NetCon.type = ESPCONN_UDP; //通信协议:UDP
ST_NetCon.proto.udp = (esp_udp *)os_zalloc(sizeof(esp_udp)); // 申请内存
//ST_NetCon.proto.udp->local_port = espconn_port(); //获取8266可用端口,这是动态的端口
ST_NetCon.proto.udp->local_port = 8266 ; // 设置本地端口
espconn_regist_sentcb(&ST_NetCon,UDP_send_callback); // 注册网络数据发送成功的回调函数
espconn_regist_recvcb(&ST_NetCon,UDP_recv_callback); // 注册网络数据接收成功的回调函数
espconn_create(&ST_NetCon); //初始化UDP通信
}
作为客户端:
- proto.udp 提供本地端口local port,远端IP和port;
- 就可以espconn_send,主动发送UDP数据报;
//初始化UDP通信,端口8266;
void ICACHE_FLASH_ATTR UDP_port8266_init()
{
ST_NetCon.type = ESPCONN_UDP; //通信协议:UDP
ST_NetCon.proto.udp = (esp_udp *)os_zalloc(sizeof(esp_udp)); // 申请内存
//ST_NetCon.proto.udp->local_port = espconn_port(); //获取8266可用端口,这是动态的端口
ST_NetCon.proto.udp->local_port = 8266 ; // 设置本地端口
ST_NetCon.proto.udp->remote_ip[0] = 192;
ST_NetCon.proto.udp->remote_ip[1] = 168;
ST_NetCon.proto.udp->remote_ip[2] = 4;
ST_NetCon.proto.udp->remote_ip[3] = 2;
ST_NetCon.proto.udp->remote_port = 6666;
espconn_regist_sentcb(&ST_NetCon,UDP_send_callback); // 注册网络数据发送成功的回调函数
espconn_regist_recvcb(&ST_NetCon,UDP_recv_callback); // 注册网络数据接收成功的回调函数
espconn_create(&ST_NetCon); //初始化UDP通信
espconn_send(&ST_NetCon,"I am ESP8266.",strlen("I am ESP8266."));
}