乐鑫Esp32学习之旅⑧ esp32上实现本地 UDP 客户端和服务端角色,在局域网内实现通讯。(附带Demo)
Posted 半颗心脏
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了乐鑫Esp32学习之旅⑧ esp32上实现本地 UDP 客户端和服务端角色,在局域网内实现通讯。(附带Demo)相关的知识,希望对你有一定的参考价值。
本系列博客学习由非官方人员 半颗心脏 潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途。如有不对之处,请留言,本人及时更改。
一. 前言;
- 关于
Esp32
的学习,最近又落下了!心里有点不舒服,今天赶紧学习下demo
,那么本篇带来的是esp32
上实现UDP
的客户端和服务器角色,可以在本地局域网和上位机或者其他一样协议的设备通讯! - 关于
UDP
的具体的协议,我就不多具体多说了! 或许你可以看看我之前在esp8266
上提到的UDP
内容!点击查看
二. UDP Client
客户端;
- 效果截图:
2.1 网络通讯常识和逻辑过程!
我们知道,任何一个
socket
通讯,都是需要IP
地址和port
端口号的,那么我们的UDP Client
的话,本地的IP
地址和port
端口号是默认为路由器分配的,而远程端口号是8265
,服务器的地址我却选择255.255.255.255
,意思是不指定局域网内的某一设备,局域网所有的设备如果监听了这个端口号,那么都可以收到esp32
发来的消息哦!!如果你要指定的
IP
地址的设备,那么就需要指定明确的地址,比如192.168.1.102
类似!
下面开始说到代码实现的逻辑过程!
第一步:上电后连接路由器,获取路由器分配的
IP
地址!第二步:系统消息监听,如果收到
IP
地址成功获取的回调,则开始创建socket
!第三步: 涉及到要连接服务器,是否存在?所以先判断下是否存在先,这样就比较全面,虽然说
UDP
是不可靠的,但是这样做,可以避免许多事情!或者连接成功路由器之后直接发送到指定的地址,不管是否存在!第四步:一旦服务器有心跳 ,我这里每时隔
3s
发送一个字符串到服务器!
2.2 代码过程!
- ①. 连接路由器:
//wifi初始化,连接路由器
void wifi_init_sta()
udp_event_group = xEventGroupCreate();
tcpip_adapter_init();
ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
wifi_config_t wifi_config =
.sta = .ssid = GATEWAY_SSID,
.password = GATEWAY_PASSWORD ;
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "wifi_init_sta finished.");
ESP_LOGI(TAG, "connect to ap SSID:%s password:%s \\n",
GATEWAY_SSID, GATEWAY_PASSWORD);
- ②. 主要代码实现:
void udp_conn(void *pvParameters)
ESP_LOGI(TAG, "task udp_conn start... \\n\\r");
//等待是否已经成功连接到路由器的标志位
xEventGroupWaitBits(udp_event_group, WIFI_CONNECTED_BIT, false, true,
portMAX_DELAY);
//5秒之后开始创建 socket
ESP_LOGI(TAG,"esp32 is ready !!! create udp client or connect servece after 5s... \\n\\r");
vTaskDelay(5000 / portTICK_RATE_MS);
//创建客户端并且检查是否创建成功
ESP_LOGI(TAG, "Now Let us create udp client ... \\n\\r");
if (create_udp_client() == ESP_FAIL)
ESP_LOGI(TAG, "client create socket error , stop !!! \\n\\r");
vTaskDelete(NULL);
else
ESP_LOGI(TAG, "client create socket Succeed !!! \\n\\r");
//创建一个发送和接收数据的任务
TaskHandle_t tx_rx_task;
xTaskCreate(&send_recv_data, "send_recv_data", 4096, NULL, 4, &tx_rx_task);
//等待 UDP连接成功标志位
xEventGroupWaitBits(udp_event_group, UDP_CONNCETED_SUCCESS, false, true,
portMAX_DELAY);
int bps;
//下面要发送的消息
char sendBuff[1024] = "hello xuhong,I am from Esp32 ...";
while (1)
total_data = 0;
vTaskDelay(3000 / portTICK_RATE_MS);
//时隔三秒发送一次数据
send_Buff_with_UDP(sendBuff, 1024);
bps = total_data / 3;
if (total_data <= 0)
int err_ret = check_connected_socket();
if (err_ret == -1)
//如果发送失败,则关闭 socket
ESP_LOGW(TAG,"udp send & recv stop !!! will close socket ... \\n\\r");
close_socket();
break;
//心跳
ESP_LOGI(TAG, "udp recv %d byte per sec! total pack: %d \\n\\r", bps,
success_pack);
vTaskDelete(tx_rx_task);
vTaskDelete(NULL);
- ③. 客户端创建的核心代码:
esp_err_t create_udp_client()
ESP_LOGI(TAG, "create_udp_client()");
//打印下要连接的服务器地址
ESP_LOGI(TAG, "connecting to %s:%d",SERVER_IP, SERVICE_PORT);
mysocket = socket(AF_INET, SOCK_DGRAM, 0);
if (mysocket < 0)
show_socket_error_reason(mysocket);
return ESP_FAIL;
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(SERVICE_PORT);
remote_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
return ESP_OK;
④. 发送和接收的方法函数:
发送方法:
sendto(mysocket, sendBuff, 1024, 0, (struct sockaddr *) &remote_addr,
,其中
sizeof(remote_addr));sendBuff
是发送的数据。- 接收方法:
recvfrom(mysocket, databuff, sizeof(databuff), 0,
,其中
(struct sockaddr *) &remote_addr, &socklen);databuff
是接收的数据。
三. UDP Server
服务端;
- 效果截图:
3.1 开启服务端的注意点:
①:作为服务器端,无疑是自己作为
AP
热点模式为好,这样的好处在于客户端连接进来之后,当前网关的IP
地址就是服务器地址;虽然说不作为热点模式也可以开启服务器端,但是这样不容易获取esp32
的地址呢!②:服务器端的要点在于监听一个端口,等待客户端的连接,之后彼此通讯。
③:实现过程就是先开启热点模式,成功之后,创建服务器端,监听某个端口号;
3.2 代码实现:
- 第一步:初始化热点模式并且开启:
//wifi的softap初始化
void wifi_init_softap()
udp_event_group = xEventGroupCreate();
tcpip_adapter_init();
ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
//设置wifi的名字和密码,注意这个是开启的wifi热点配置,不是要配置连接的路由器账号密码
wifi_config_t wifi_config = .ap =
.ssid = AP_SSID,
.ssid_len = 0,
.password = AP_PAW,
.authmode = WIFI_AUTH_WPA_WPA2_PSK ;
if (strlen(AP_SSID) == 0)
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
//设置当前模式为AP模式
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
//配置信息
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
//开始执行
ESP_ERROR_CHECK(esp_wifi_start());
- ②:创建一个
UDP
服务器端,剩下的步骤和客户端也就一样了;
esp_err_t create_udp_server()
ESP_LOGI(TAG, "Create Udp Server succeed port : %d \\n", SERVICE_PORT);
mysocket = socket(AF_INET, SOCK_DGRAM, 0);
if (mysocket < 0)
show_socket_error_reason(mysocket);
return ESP_FAIL;
//指定连接的服务器IP地址和端口号
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVICE_PORT);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(mysocket, (struct sockaddr *) &server_addr, sizeof(server_addr))
< 0)
show_socket_error_reason(mysocket);
close(mysocket);
return ESP_FAIL;
return ESP_OK;
四. 代码细节;
esp32
的代码结构还是比较人性化的,在熟悉代码过程中,不断轮询这个系统信息,每个ID
对应的信息不一样的,这样很符合面对对象的编程,不断有回调信息,只需要监听这个方法返回的数据即可知道系统在干嘛的了!
static esp_err_t event_handler(void *ctx, system_event_t *event)
switch (event->event_id)
//station模式开启回调
case SYSTEM_EVENT_STA_START:
esp_wifi_connect();
break;
//断开与路由器的连接
case SYSTEM_EVENT_STA_DISCONNECTED:
esp_wifi_connect();
xEventGroupClearBits(udp_event_group, WIFI_CONNECTED_BIT);
break;
//成功连接到路由器,获取到Ip地址
case SYSTEM_EVENT_STA_GOT_IP:
ESP_LOGI(TAG, "event_handler:SYSTEM_EVENT_STA_GOT_IP!");
ESP_LOGI(TAG, "got ip:%s\\n",
ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip));
xEventGroupSetBits(udp_event_group, WIFI_CONNECTED_BIT);
break;
//作为AP热点模式,检测到有子设备接入
case SYSTEM_EVENT_AP_STACONNECTED:
ESP_LOGI(TAG, "station:"MACSTR" join,AID=%d\\n",
MAC2STR(event->event_info.sta_connected.mac),
event->event_info.sta_connected.aid);
xEventGroupSetBits(udp_event_group, WIFI_CONNECTED_BIT);
break;
//作为AP热点模式,检测到有子设备断开了连接
case SYSTEM_EVENT_AP_STADISCONNECTED:
ESP_LOGI(TAG, "station:"MACSTR"leave,AID=%d\\n",
MAC2STR(event->event_info.sta_disconnected.mac),
event->event_info.sta_disconnected.aid);
xEventGroupSetBits(udp_event_group, UDP_CONNCETED_SUCCESS);
xEventGroupClearBits(udp_event_group, WIFI_CONNECTED_BIT);
break;
default:
break;
return ESP_OK;
rtos
的xEventGroupSetBits()
和xEventGroupWaitBits()
是一对一的!当调用xEventGroupWaitBits()
时候,会处于阻塞等待,直到xEventGroupSetBits()
发送标志位才会执行下面的代码!
- 宏定义的一些参数:注意下面的
255.255.255
是指局域网不指定设备的地址!
//全局声明:是否server服务器或者station客户端 ------> true为服务器,false为客户端
#define Server_Station_Option false
/*
* 要连接的路由器名字和密码
*/
//路由器的名字
#define GATEWAY_SSID "AliyunOnlyTest"
//连接的路由器密码
#define GATEWAY_PASSWORD "aliyun#123456"
//数据包大小
#define EXAMPLE_DEFAULT_PKTSIZE 1024
/*
* 自己作为AP热点时候,配置信息如下
*/
//ssid
#define AP_SSID "XuHong_Esp32"
//密码
#define AP_PAW "xuhong123456"
//最大连接数
#define EXAMPLE_MAX_STA_CONN 1
/*
* station模式时候,服务器地址配置
*/
//服务器的地址:这里的 255.255.255.255是在局域网发送,不指定某个设备
#define SERVER_IP "255.255.255.255"
//端口号
#define SERVICE_PORT 8265
五. 其他;
- 当执行
make flash
发生错误时候LOG
如下,说明在windows
下找不到这个串口,检查下esp32
是否正常连接到电脑!注意在windows
下设置的端口号应该是COM
+端口号,比如COM2
。
raise SerialException("could not open port !r: !r".format(self.portstr, ctypes.WinError()))
serial.serialutil.SerialException: could not open port 'COM2': WindowsError(2, '\\xcf\\xb5\\xcd\\xb3\\xd5\\xd2\\xb2\\xbb\\xb5\\xbd\\xd6\\xb8\\xb6\\xa8\\xb5\\xc4\\xce\\xc4\\xbc\\xfe\\xa1\\xa3')
make: *** [/home/Administrator/esp-idf/components/esptool_py/Makefile.projbuild:55:flash] 错误 1
注意运行工程时候的
XuHongUDP.h
文件的宏定义Server_Station_Option
是服务器端和客户端的切换开关!本博文硬件代码下载:https://download.csdn.net/download/xh870189248/10487751
esp32汇总工程,欢迎star,收到第一更新信息:https://github.com/xuhongv/StudyInEsp32
以上是关于乐鑫Esp32学习之旅⑧ esp32上实现本地 UDP 客户端和服务端角色,在局域网内实现通讯。(附带Demo)的主要内容,如果未能解决你的问题,请参考以下文章
乐鑫Esp32学习之旅28 分享在 esp32 SDK实现驱动 Flash W25Q32 的封装,扩大容量存储更多的资源。(开源源码工程)。
乐鑫Esp32学习之旅 乐鑫 ESP-S2/S3 模组的实现 USB 无线网卡上网,为你的台式机装上无线WiFI上网吧。(附带源码)
乐鑫Esp32学习之旅 乐鑫 ESP-S2/S3 模组的实现 USB 无线网卡上网,为你的台式机装上无线WiFI上网吧。(附带源码)
乐鑫Esp32学习之旅 乐鑫 ESP-S2/S3 模组的实现 USB 无线网卡上网,为你的台式机装上无线WiFI上网吧。(附带源码)
乐鑫Esp32学习之旅28 分享在 esp32 SDK实现驱动 Flash W25Q32 的封装,扩大容量存储更多的资源。(开源源码工程)。
乐鑫Esp32学习之旅28 分享在 esp32 SDK实现驱动 Flash W25Q32 的封装,扩大容量存储更多的资源。(开源源码工程)。