第五十二章 ESP32S3 UDP 实验

对于 lwIP 的 Socket 的使用方式,它与文件操作非常相似。在文件操作中,首先打开文件,然后进行读/写操作,最后关闭文件。在TCP/IP网络通信中,也存在着相同的操作流程,但所使用的接口不再是文件描述符或 FILE*,而是被称为 Socket 的描述符。通过 Socket,可以进行读、写、打开和关闭操作来进行网络数据的传输。此外,还有一些辅助函数,如查询域名/IP 地址和设置 Socket 功能等。在本章中,我们将使用 Socket 编程接口来实现 UDP 实验。

本章分为如下几个部分:

52.1 Socket 编程 UDP 连接流程

52.2 硬件设计

52.3 软件设计

52.4 下载验证

52.1 Socket 编程 UDP 连接流程

在实现 UDP协议之前,用户需要按照以下步骤配置结构体 sockaddr_in的成员变量,以便建

立 UDP 连接:

①:配置 ESP32-S3 设备连接网络(必须的,因为 WiFi 是无线通信,所以需搭建通信桥梁)。

②:将 sin_family 设置为 AF_INET,表示使用 IPv4 网络协议。

③:设置 sin_port 为所需的端口号,例如 8080。

④:设置 sin_addr.s_addr 为本地 IP 地址。

⑤:调用函数 Socket 创建 Socket 连接。请注意,该函数的第二个参数指定连接类型。

SOCK_STREAM 表示 TCP 连接,而 SOCK_DGRAM 表示 UDP 连接。

⑥:调用函数 bind 将本地服务器地址与 Socket 进行绑定。

⑦:调用适当的收发函数来接收或发送数据。

通过遵循这些步骤,用户可以成功地配置并建立 UDP 连接,以实现数据的发送和接收。

52.2 硬件设计
  1. 例程功能

本章实验功能简介:

本实验主要通过 Socket 编程接口实现了一个 UDP 服务器。这个服务器具有以下功能:

①:可以通过按键发送 UDP 广播数据给其他 UDP 客户端。

②:能够接收其他 UDP 客户端发送的广播数据。

③:实时将接收到的数据显示在 LCD 屏幕上。

通过这个实验,可深入了解 UDP 协议的工作原理,并掌握如何使用 Socket 编程接口来实现UDP通信。这对于开发基于UDP的网络应用程序非常有用,例如实时通信、多播应用等。

  1. 硬件资源

1) LED 灯

LED-IO1

2) XL9555

IIC_INT-IO0(需在 P5 连接 IO0)

IIC_SDA-IO41

IIC_SCL-IO42

3) SPILCD

CS-IO21

SCK-IO12

SDA-IO11

DC-IO40(在 P5 端口,使用跳线帽将 IO_SET 和 LCD_DC 相连)

PWR- IO1_3(XL9555)

RST- IO1_2(XL9555)

4) ESP32-S3 内部 WiFi

  1. 原理图

本章实验使用的 WiFi 为 ESP32-S3 的片上资源,因此并没有相应的连接原理图。

52.3 软件设计

52.3.1 程序流程图

本实验的程序流程图:

图 52.3.1.1 程序流程图

52.3.2 程序解析

在本章节中,主要关注两个文件: lwip_demo.c 和 lwip_demo.h。 lwip_demo.h 文件主要定义了发送标志位并声明了 lwip_demo 函数,这部分相对简单,所以暂不详细解释。主要关注点是lwip_demo.c 文件中的函数。在 lwip_demo 函数中,我们配置了相关的 UDP 参数,并创建了一个名为 lwip_send_thread 的发送数据线程。这个线程通过调用 scokec 函数来发送数据到服务器。接下来,将分别详细解释 lwip_demo 函数和 lwip_send_thread 任务。

复制代码
/* 需要自己设置远程IP地址 */
#define IP_ADDR   "192.168.1.2"

#define LWIP_DEMO_RX_BUFSIZE         200    /* 最大接收数据长度 */
#define LWIP_DEMO_PORT               8080   /* 连接的本地端口号 */
#define LWIP_SEND_THREAD_PRIO       ( tskIDLE_PRIORITY + 3 ) /* 发送数据线程优先级 */

/* 接收数据缓冲区 */
uint8_t g_lwip_demo_recvbuf[LWIP_DEMO_RX_BUFSIZE]; 
/* 发送数据内容 */
char g_lwip_demo_sendbuf[] = "I am ESP32 S3.\r\n";
/* 数据发送标志位 */
uint8_t g_lwip_send_flag;
static struct sockaddr_in dest_addr;            /* 远端地址 */
struct sockaddr_in g_local_info;
socklen_t g_sock_fd;                            /* 定义一个Socket接口 */
static void lwip_send_thread(void *arg);
extern QueueHandle_t g_display_queue;           /* 显示消息队列句柄 */


/**
 * @brief       发送数据线程
 * @param       无
 * @retval      无
 */
void lwip_data_send(void)
{
    xTaskCreate(lwip_send_thread, "lwip_send_thread", 4096, NULL, LWIP_SEND_THREAD_PRIO, NULL);
}

/**
 * @brief       lwip_demo实验入口
 * @param       无
 * @retval      无
 */
void lwip_demo(void)
{
    char *tbuf;
    lwip_data_send();                                           /* 创建发送数据线程 */
    /* 远端参数设置 */
    dest_addr.sin_addr.s_addr = inet_addr(IP_ADDR);             /* 目标地址 */
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(LWIP_DEMO_PORT);                 /* 目标端口 */
    
    g_local_info.sin_family = AF_INET;                          /* IPv4地址 */
    g_local_info.sin_port = htons(LWIP_DEMO_PORT);              /* 设置端口号 */
    g_local_info.sin_addr.s_addr = htons(INADDR_ANY);           /* 设置本地IP地址 */

    g_sock_fd = socket(AF_INET, SOCK_DGRAM, 0);                 /* 建立一个新的socket连接 */
    
    tbuf = malloc(200);                                         /* 申请内存 */
    sprintf((char *)tbuf, "Port:%d", LWIP_DEMO_PORT);           /* 客户端端口号 */
    spilcd_show_string(0, 170, 200, 16, 16, tbuf, MAGENTA);
    
    /* 建立绑定 */
    bind(g_sock_fd, (struct sockaddr *)&g_local_info, sizeof(g_local_info));
	
    while (1)
    {
		memset(g_lwip_demo_recvbuf, 0, sizeof(g_lwip_demo_recvbuf));
        int rcvDataSize = recv(g_sock_fd, (void *)g_lwip_demo_recvbuf, sizeof(g_lwip_demo_recvbuf), 0);
		if (rcvDataSize > 0)
		{
			printf("%s\r\n", g_lwip_demo_recvbuf);
		}
    }
}

/**
 * @brief       发送数据线程函数
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void lwip_send_thread(void *pvParameters)
{
    pvParameters = pvParameters;

    while (1)
    {
        if ((g_lwip_send_flag & LWIP_SEND_DATA) == LWIP_SEND_DATA)      /* 有数据要发送 */
        {
            // printf("send\r\n");
            sendto(g_sock_fd,                                           /* scoket */
                  (char *)g_lwip_demo_sendbuf,                          /* 发送的数据 */
                  sizeof(g_lwip_demo_sendbuf), 0,                       /* 发送的数据大小 */
                  (struct sockaddr *)&dest_addr,                        /* 接收端地址信息 */ 
                  sizeof(dest_addr));                                   /* 接收端地址信息大小 */

            g_lwip_send_flag &= ~LWIP_SEND_DATA;
        }
        
        vTaskDelay(100);
   }
}

在源码中, lwip_demo函数通过 lwip_data_send创建了发送数据的线程 lwip_send_thread,并配置了 Socket 的 UDP 协议。该线程在发送前会检查标志位,有效时则通过 sendto 发送数据并重置标志位。同时,需设置目标 IP 地址以确保数据正确发送。此外,主函数的循环中不断通过recv 接收数据并使用串口输出接收的数据。

52.4 下载验证

实验前需要在程序中,将相关配置修改为自己的配置,具体下面两点:

  • Wifi账号和密码:首先需要设置好能够连接的网络账号(将宏DEFAULT_SSID修改为自己的账号,将宏DEFAULT_PWD修改为自己的密码);
  • 电脑IP地址:设置自己电脑的远程IP地址(修改宏IP_ADDR为自己电脑IP地址,方法是在Windows的命令提示符中输入ipconfig,就会看到电脑IP地址)。

然后,使用电脑作为终端,确保它与 ESP32-S3 设备处于同一网络段内。当 ESP32-S3 设备成功连接到网络时,它的 LCD 显示屏上会显示相应的内容:

图 52.4.1 设备连接到网络时 LCD 显示的信息

打开网络调试助手,然后配置网络参数,如 UDP 协议、端口号、目标主机设置等,设置内容如下图所示,在确保网络连接正常后,通过按下开发板上的 KEY0 按键来发送数据(存在数组g_lwip_demo_sendbuf\[\] = "I am ESP32 S3.\r\n";)至网络调试助手。

当网络调试助手接收到开发板发送的字符串时,它会在显示区域展示这个信息。此外,用户还可以在调试助手的发送区域输入要发送的数据,然后点击发送键("I am a computer."),将数据发送至ESP32-S3 设备。此时, ESP32-S3 的串口将打印接收到的数据,具体操作和输出如下图所示。

图 52.4.3 接收网络调试助手的数据

相关推荐
沸速存储4 分钟前
CPU 和 GPU 核心差别在哪?为什么 AI 训练离不开 GPU
服务器·人工智能·科技·嵌入式硬件·电脑
路由侠内网穿透1 小时前
本地部署企业级快速开发平台芋道管理后台并实现外部访问
运维·服务器·网络·网络协议
basketball6161 小时前
软考中级网络工程师 第4章 无线通信网 备考总结
linux·服务器·网络·网络工程师·软考
我不会JAVA!1 小时前
【无标题】
嵌入式硬件·h.264
芯岭技术郦1 小时前
OM6625A芯片 Timer 定时器计数模式使用说明
stm32·单片机·嵌入式硬件
Dr.kangder3 小时前
嵌入式面试总结(八)——大小端
嵌入式硬件·面试·职场和发展·架构·嵌入式
睡一觉就好了。3 小时前
Linux 信号机制
linux·运维·网络
zlinear数据采集卡5 小时前
D223的SRAM硬件记录仪:外部触发与瞬态波形捕获方案
arm开发·嵌入式硬件·fpga开发·开源
zcmodeltech5 小时前
双碳沙盘模型多系统协同控制系统设计:基于STM32与Modbus RTU的碳捕集-零碳园区-综合能源全场景联动方案
stm32·嵌入式硬件·能源·嵌入式实时数据库·多分类
LCG米5 小时前
STM32零基础入门教程:从环境搭建到实战项目(点亮LED + 串口打印 + 呼吸灯)
stm32·单片机·嵌入式硬件