首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

乐鑫Esp32学习之旅⑧ esp32上实现本地 UDP 客户端和服务端角色,在局域网内实现通讯。(附带Demo)

  • 23-11-14 06:41
  • 3865
  • 7663
blog.csdn.net

  • 本系列博客学习由非官方人员 半颗心脏 潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途。如有不对之处,请留言,本人及时更改。

    • 1、 爬坑学习新旅程,虚拟机搭建esp32开发环境,打印 “Hellow World”。

    • 2、 巧用eclipes编辑器,官方教程在在Windows下搭建esp32开发环境,打印 “Hellow World”。

    • 3、 认识基本esp32的GPIO接口,开始点亮您的第一盏 LED和中断回调实现按键功能 。

    • 4、体会esp32的强大的定时器功能, 实现定时2s闪烁一盏LED灯。

    • 5、接触实践esp32的pwm宽度脉冲功能, 实现呼吸效果闪烁一盏LED灯。

    • 6、smartConfig和微信airKiss在esp32的实现,一键配网轻松快捷连接路由器。

    • 7、利用GPIO中断做一个按键的短按和长按的回调事件,再也无须担心触发源。

    • 8、esp32上实现本地 UDP 客户端和服务端角色,在局域网内实现通讯。

    • 9、esp32上实现本地 TCP 客户端和服务端角色,可断线重连原路返回数据。


一. 前言;


      • 一. 前言;
      • 二. UDP Client客户端;
        • 2.1 网络通讯常识和逻辑过程!
        • 2.2 代码过程!
      • 三. UDP Server服务端;
        • 3.1 开启服务端的注意点:
        • 3.2 代码实现:
      • 四. 代码细节;
      • 五. 其他;

  • 关于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 
",
    GATEWAY_SSID, GATEWAY_PASSWORD);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

  • ②. 主要代码实现:
void udp_conn(void *pvParameters) {

    ESP_LOGI(TAG, "task udp_conn start... 

");

    //等待是否已经成功连接到路由器的标志位
    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... 

");
    vTaskDelay(5000 / portTICK_RATE_MS);

    //创建客户端并且检查是否创建成功
    ESP_LOGI(TAG, "Now Let us create udp client ... 

");
    if (create_udp_client() == ESP_FAIL) {
        ESP_LOGI(TAG, "client create socket error , stop !!! 

");
        vTaskDelete(NULL);
    } else {
        ESP_LOGI(TAG, "client create socket Succeed  !!! 

");
    }


    //创建一个发送和接收数据的任务
    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 ... 

");
                close_socket();
                break;
            }
        }
        //心跳
        ESP_LOGI(TAG, "udp recv %d byte per sec! total pack: %d 

", bps,
                success_pack);

    }

    vTaskDelete(tx_rx_task);
    vTaskDelete(NULL);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

  • ③. 客户端创建的核心代码:
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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

  • ④. 发送和接收的方法函数:

  • 发送方法: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());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

  • ②:创建一个UDP服务器端,剩下的步骤和客户端也就一样了;

esp_err_t create_udp_server() {

    ESP_LOGI(TAG, "Create Udp Server succeed port : %d 
", 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

四. 代码细节;


  • 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
",
                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
",
                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
",
                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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

  • 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

五. 其他;


  • 当执行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, 'xcfxb5xcdxb3xd5xd2xb2xbbxb5xbdxd6xb8xb6xa8xb5xc4xcexc4xbcxfexa1xa3')
make: *** [/home/Administrator/esp-idf/components/esptool_py/Makefile.projbuild:55:flash] 错误 1
  • 1
  • 2
  • 3
  • 4

  • 注意运行工程时候的XuHongUDP.h文件的宏定义Server_Station_Option是服务器端和客户端的切换开关!

  • 本博文硬件代码下载:https://download.csdn.net/download/xh870189248/10487751

  • esp32汇总工程,欢迎star,收到第一更新信息:https://github.com/xuhongv/StudyInEsp32

注:本文转载自blog.csdn.net的半颗心脏的文章"https://blog.csdn.net/xh870189248/article/details/80737111"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top