云计算百科
云计算领域专业知识百科平台

ESP32 使用ESP-IDF 连接WiFi并使用UDP通信源码分享

ESP32 使用ESP-IDF 连接WiFi并使用UDP通信源码分享

  • 一、源码分享
    • 1、效果展示
    • 2、开发环境搭建
    • 3、源码分享
  • 二、UDP协议介绍
    • 1、主要特点
    • 2、适用场景
    • 3、与TCP的主要区别
    • 4、总结
  • 三、ESP-IDF详解
    • 1、ESP32 概述
    • 2、 ESP-IDF 详解
      • 2.1 、ESP-IDF 的核心组件与架构
      • 2.2 、ESP-IDF 开发环境与工具链
      • 2.3 、ESP-IDF 开发流程简述
      • 2.4、 示例代码结构 (最简单的 Hello World)
      • 2.5、 优势与特点
      • 2.6 、适用场景
    • 3、总结

在这里插入图片描述

一、源码分享

1、效果展示

在这里插入图片描述

2、开发环境搭建

参考我这篇博文:VS Code 在线安装ESP-IDF,ESP32开发环境搭建详细教程

3、源码分享

/* WiFi station Example

This example code is in the Public Domain (or CC0 licensed, at your option.)

Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"

#include "lwip/err.h"
#include "lwip/sys.h"
#include "esp_netif.h"
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include "lwip/err.h"
#include "lwip/sys.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
/* The examples use WiFi configuration that you can set via project configuration menu

If you'd rather not, just change the below entries to strings with
the config you want – ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#define EXAMPLE_ESP_WIFI_SSID "VIP"
#define EXAMPLE_ESP_WIFI_PASS "nimingzi"
#define EXAMPLE_ESP_MAXIMUM_RETRY 5

#if CONFIG_ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HUNT_AND_PECK
#define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_HUNT_AND_PECK
#define EXAMPLE_H2E_IDENTIFIER ""
#elif CONFIG_ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HASH_TO_ELEMENT
#define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_HASH_TO_ELEMENT
#define EXAMPLE_H2E_IDENTIFIER CONFIG_ESP_WIFI_PW_ID
#elif CONFIG_ESP_STATION_EXAMPLE_WPA3_SAE_PWE_BOTH
#define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_BOTH
#define EXAMPLE_H2E_IDENTIFIER CONFIG_ESP_WIFI_PW_ID
#endif
#if CONFIG_ESP_WIFI_AUTH_OPEN
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_OPEN
#elif CONFIG_ESP_WIFI_AUTH_WEP
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WEP
#elif CONFIG_ESP_WIFI_AUTH_WPA_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA2_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA_WPA2_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA3_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA3_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA2_WPA3_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_WPA3_PSK
#elif CONFIG_ESP_WIFI_AUTH_WAPI_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WAPI_PSK
#endif

static EventGroupHandle_t s_wifi_event_group;

#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1

static const char *TAG = "wifi station";

static int s_retry_num = 0;

static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
ESP_LOGI(TAG,"connect to the AP fail");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}

void wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate();

ESP_ERROR_CHECK(esp_netif_init());

ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();

wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));

esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));

wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.password = EXAMPLE_ESP_WIFI_PASS,
.threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD,
.sae_pwe_h2e = ESP_WIFI_SAE_MODE,
.sae_h2e_identifier = EXAMPLE_H2E_IDENTIFIER,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );

ESP_LOGI(TAG, "wifi_init_sta finished.");

EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);

if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
}
/* 需要自己设置远程IP地址 */
#define IP_ADDR "192.168.11.218"
#define PORT 6000 /* 连接的本地端口号 */

void udp_loop()
{
static struct sockaddr_in destAddr; /* 远端地址 */
struct sockaddr_in localInfo;
socklen_t fd; /* 定义一个Socket接口 */
uint8_t recvBuf[512];
/* 远端参数设置 */
destAddr.sin_addr.s_addr = inet_addr(IP_ADDR); /* 目标地址 */
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(PORT); /* 目标端口 */

localInfo.sin_family = AF_INET; /* IPv4地址 */
localInfo.sin_port = htons(PORT); /* 设置端口号 */
localInfo.sin_addr.s_addr = htons(INADDR_ANY); /* 设置本地IP地址 */

fd = socket(AF_INET, SOCK_DGRAM, 0); /* 建立一个新的socket连接 */

/* 建立绑定 */
bind(fd, (struct sockaddr *)&localInfo, sizeof(localInfo));

while (1)
{
memset(recvBuf, 0, sizeof(recvBuf));
int recvLen = recv(fd, (void *)recvBuf, sizeof(recvBuf), 0);
printf("%s\\r\\n",recvBuf);
sendto(fd, /* scoket */
(char *)recvBuf, /* 发送的数据 */
recvLen, 0, /* 发送的数据大小 */
(struct sockaddr *)&destAddr, /* 接收端地址信息 */
sizeof(destAddr)); /* 接收端地址信息大小 */
}
}

void app_main(void)
{
//Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);

if (CONFIG_LOG_MAXIMUM_LEVEL > CONFIG_LOG_DEFAULT_LEVEL) {
esp_log_level_set("wifi", CONFIG_LOG_MAXIMUM_LEVEL);
}

ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
wifi_init_sta();

vTaskDelay(3000 / portTICK_PERIOD_MS);
printf("udp start\\n");
while(1)
{
udp_loop();
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}

二、UDP协议介绍

UDP(User Datagram Protocol,用户数据报协议)是互联网协议族(TCP/IP)中的一个核心协议,位于传输层。它提供了一种无连接的、不可靠的数据传输服务。

1、主要特点

  • 无连接性:UDP在发送数据之前不需要像TCP那样先建立连接。发送方直接构造数据报并将其发送出去,接收方也无需确认。这使得通信的开销很小。

  • 不可靠性:UDP不保证数据报一定能够到达目的地,也不保证数据报到达的顺序与发送的顺序一致。它不提供确认、重传、流量控制和拥塞控制机制。如果应用程序需要可靠性,必须在应用层自行实现。

  • 面向报文:UDP对应用程序交下来的报文,添加UDP头部后就交付给网络层。接收方的UDP层在去除头部后,将完整的报文交付给应用进程。UDP既不合并报文,也不拆分报文,而是保留报文的边界。

  • 头部开销小:UDP头部非常简单,只有8个字节(64位),包含以下字段:

    • 源端口(Source Port,16位)
    • 目的端口(Destination Port,16位)
    • 长度(Length,16位):表示UDP数据报(包括头部和数据部分)的总长度,单位是字节。
    • 校验和(Checksum,16位):用于检测数据在传输过程中是否出错(可选,IPv4中可置为0表示不计算,IPv6中强制要求)。

    U

    D

    P

    头部结构

    =

    源端口

    目的端口

    长度

    校验和

    (

    16

    )

    (

    16

    )

    (

    16

    )

    (

    16

    )

    UDP头部结构 = \\begin{array}{|c|c|c|c|} \\hline 源端口 & 目的端口 & 长度 & 校验和 \\\\ \\hline (16位) & (16位) & (16位) & (16位) \\\\ \\hline \\end{array}

    UDP头部结构=源端口(16)目的端口(16)长度(16)校验和(16)

  • 高效性:由于没有建立连接、确认、重传等复杂机制,UDP协议本身的开销很小,传输效率高。

  • 2、适用场景

    由于UDP的特点,它非常适用于以下类型的应用:

    • 对实时性要求高,对可靠性要求相对较低的应用:如实时音视频传输(VoIP、视频会议、直播)、在线游戏等。少量的丢包可能表现为短暂的卡顿或杂音,通常是可以接受的,而重传带来的延迟则是不可接受的。
    • 简单查询/应答应用:如DNS域名解析、DHCP动态主机配置协议、SNMP简单网络管理协议等。这类应用通常只需要发送一个小的请求包并等待一个小的响应包。
    • 多播和广播应用:UDP天然支持将数据包发送给多个接收者(多播)或子网内的所有主机(广播),这在很多网络服务和发现协议(如路由协议OSPF的部分信息)中非常有用。

    3、与TCP的主要区别

    下表总结了UDP与TCP的主要区别:

    特性UDPTCP
    连接性 无连接 面向连接
    可靠性 不可靠(不保证送达、不保证顺序) 可靠(保证送达、保证顺序)
    传输单位 数据报(报文) 字节流
    流量控制 有(滑动窗口)
    拥塞控制
    头部开销 小(8字节) 大(通常20字节,可选项最多40字节)

    4、总结

    UDP协议以其简单、高效、无连接的特点,在特定的网络应用场景中发挥着不可替代的作用。它适合那些需要低延迟、能够容忍少量丢包、或者需要广播/多播功能的应用。开发者需要根据应用的具体需求,权衡可靠性与效率,选择合适的传输层协议。

    三、ESP-IDF详解

    1、ESP32 概述

    ESP32 是由乐鑫科技(Espressif Systems)推出的一款高性能、低功耗、高集成度的 Wi-Fi & 蓝牙双模系统级芯片(SoC)。它广泛应用于物联网(IoT)、智能家居、工业控制等领域。其关键特性包括:

    • 双核处理器(通常为 Xtensa LX6,某些型号为 RISC-V)
    • 集成 Wi-Fi (802.11 b/g/n) 和蓝牙 (包括经典蓝牙和低功耗蓝牙 BLE)
    • 丰富的外设接口:

      S

      P

      I

      SPI

      SPI,

      I

      2

      C

      I2C

      I2C,

      I

      2

      S

      I2S

      I2S,

      U

      A

      R

      T

      UART

      UART,

      A

      D

      C

      ADC

      ADC,

      D

      A

      C

      DAC

      DAC,

      P

      W

      M

      PWM

      PWM, 触摸传感器等

    • 充足的内存(RAM 和 Flash 选项多样)
    • 强大的安全特性(如加密加速器)
    • 超低功耗设计,支持多种休眠模式

    2、 ESP-IDF 详解

    ESP-IDF (Espressif IoT Development Framework) 是乐鑫官方为 ESP32、ESP32-S 系列、ESP32-C 系列等芯片提供的开发框架和 SDK(软件开发工具包)。它是开发基于 ESP32 芯片应用程序的首选和官方推荐工具。

    2.1 、ESP-IDF 的核心组件与架构

    ESP-IDF 是一个分层、模块化的框架:

    • 硬件抽象层 (HAL): 提供对芯片硬件资源(如 GPIO、UART、SPI、I2C、定时器、ADC、DAC、RTC、Wi-Fi、蓝牙等)进行操作的统一接口,屏蔽底层硬件细节。
    • 驱动 (Drivers): 在 HAL 之上,提供更易用、功能更丰富的设备驱动(如 SPI Flash 驱动、SD 卡驱动、以太网驱动等)。
    • FreeRTOS: ESP-IDF 深度集成了开源的 FreeRTOS 实时操作系统内核。它提供了任务管理、队列、信号量、软件定时器、中断管理等机制,充分利用 ESP32 的多核特性(任务可以在不同核心上运行)。
    • 中间件:
      • Wi-Fi 协议栈: 实现 Wi-Fi 的 Station(客户端)、SoftAP(接入点)、Promiscuous(监听)模式。
      • 蓝牙协议栈: 实现经典蓝牙和低功耗蓝牙 (BLE) 的各种角色(如 GATT Client/Server, GAP Central/Peripheral)。
      • TCP/IP 协议栈 (lwIP): 轻量级的 TCP/IP 协议栈,支持 IPv4/IPv6 (部分型号)、DHCP、DNS、Socket API 等。
      • 文件系统 (FATFS/VFS): 支持在 SPI Flash 或外部存储设备上使用 FAT 文件系统。
      • 加密库: 提供 AES、SHA、RSA 等加密算法的软件和硬件加速实现。
      • HTTP/WebSocket/MQTT 等协议库: 方便构建网络应用。
    • 应用程序: 用户编写的业务逻辑代码,运行在 FreeRTOS 的任务中,通过调用下层 API 实现功能。

    2.2 、ESP-IDF 开发环境与工具链

    • 工具链: 基于 GNU GCC 编译器(针对 Xtensa 或 RISC-V 架构)。
    • 构建系统: 使用 CMake(早期版本使用 GNU Make)。开发者编写 CMakeLists.txt 文件来定义项目结构、源文件、依赖组件等。
    • 配置工具: menuconfig。这是一个基于文本的图形化配置工具(类似于 Linux Kernel 的 make menuconfig),用于配置 ESP-IDF 的众多选项,如:
      • 选择目标芯片型号
      • 配置 Wi-Fi/BT 参数
      • 调整 FreeRTOS 设置(任务栈大小、优先级等)
      • 配置日志输出级别和方式
      • 配置内存布局
      • 启用/禁用特定功能和外设驱动
    • 核心工具 – idf.py: 这是 ESP-IDF 提供的命令行工具,用于执行构建、烧录、调试、监视串口输出等几乎所有开发任务。常用命令如:
      • idf.py set-target esp32 (设置目标芯片)
      • idf.py menuconfig (启动配置工具)
      • idf.py build (编译项目)
      • idf.py -p PORT flash (烧录固件到设备,PORT 为串口,如 COM3 或 /dev/ttyUSB0)
      • idf.py -p PORT monitor (启动串口监视器,查看设备日志输出)
      • idf.py fullclean (彻底清理构建目录)

    2.3 、ESP-IDF 开发流程简述

  • 环境搭建: 在 Windows、Linux 或 macOS 上安装 ESP-IDF 开发环境(包括工具链、Python、Git 等)。
  • 创建项目: 使用 idf.py create-project 或复制示例项目模板。
  • 编写代码: 在 main 目录下编写应用程序代码 (通常是 main.c 或 app_main() 函数)。
  • 配置项目: 运行 idf.py menuconfig 根据需求进行配置。
  • 编译项目: 运行 idf.py build 生成可执行固件 (.bin 文件)。
  • 烧录固件: 将 ESP32 开发板连接到电脑,运行 idf.py -p PORT flash 将固件烧录到设备 Flash 中。
  • 监视输出: 运行 idf.py -p PORT monitor 查看设备运行日志,进行调试。可以使用 ESP_LOGx (如 ESP_LOGI, ESP_LOGE) 函数打印不同级别的日志。
  • 2.4、 示例代码结构 (最简单的 Hello World)

    #include "freertos/FreeRTOS.h"
    #include "freertos/task.h"
    #include "esp_log.h"

    static const char *TAG = "MAIN";

    void app_main(void)
    {
    while (1) {
    ESP_LOGI(TAG, "Hello World!"); // 打印 Info 级别日志
    vTaskDelay(pdMS_TO_TICKS(1000)); // 延时 1000 毫秒 (FreeRTOS 延时)
    }
    }

    2.5、 优势与特点

    • 官方支持: 由芯片原厂维护,更新及时,与新芯片特性同步快。
    • 功能全面: 提供对 ESP32 所有硬件特性和外设的底层访问。
    • 性能优化: 针对 ESP32 硬件进行了深度优化(如 Wi-Fi/BT 共存)。
    • 稳定性高: 经过大量商业产品验证。
    • 社区活跃: 用户多,社区支持好,问题容易找到解决方案。
    • 文档完善: 官方提供详尽的 API 参考指南、编程指南和示例代码。
    • 模块化: 易于扩展和复用代码,方便管理大型项目。
    • 开源免费: 基于 Apache 2.0 许可证。

    2.6 、适用场景

    ESP-IDF 适用于需要深度控制硬件、追求高性能、高稳定性或需要使用 ESP32 特有功能(如超低功耗协处理器 ULP)的应用开发。对于快速原型开发,也可以考虑基于 ESP-IDF 构建的更高级框架(如 Arduino for ESP32、MicroPython),但这些框架最终都依赖于 ESP-IDF 的底层功能。

    3、总结

    ESP-IDF 是开发 ESP32 系列芯片的强大、灵活且功能完备的官方框架。它提供了从硬件操作到网络协议栈的全套解决方案,结合 FreeRTOS 实现了高效的多任务处理。虽然其学习曲线相对陡峭,尤其是对于不熟悉嵌入式开发和 RTOS 的开发者,但它提供了最直接、最强大的方式来充分利用 ESP32 的潜力,是开发复杂或高性能 ESP32 应用的基石。掌握 idf.py 工具链和 menuconfig 配置是使用 ESP-IDF 的关键。

    在这里插入图片描述

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » ESP32 使用ESP-IDF 连接WiFi并使用UDP通信源码分享
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!