入门篇:环境搭建与 FreeRTOS 多任务

知识库
知识库文档
/tech-stacks/esp-idf/tutorial/入门篇:环境搭建与 FreeRTOS 多任务.md

文档

ESP-IDF 开发:从环境搭建到 WiFi 应用

本章目标

搭建 ESP-IDF 开发环境,理解项目结构和组件化架构,开发第一个 WiFi + FreeRTOS 应用。


1. ESP-IDF 与 Arduino ESP32 的区别

维度 Arduino ESP32 ESP-IDF
底层 封装 ESP-IDF 直接 FreeRTOS
控制粒度 粗(简单 API) 细(完整控制)
双核利用 有限 手动指定核心
功耗优化 基本 deep-sleep + ULP
量产 不建议 官方推荐

2. 项目结构

my_app/
├── CMakeLists.txt           ← 顶层 CMake
├── sdkconfig                ← idf.py menuconfig 生成
├── main/
│   ├── CMakeLists.txt       ← idf_component_register
│   └── main.c
├── components/
│   └── my_driver/
│       ├── CMakeLists.txt   ← idf_component_register(SRCS ... REQUIRES ...)
│       ├── my_driver.c
│       └── include/my_driver.h
└── partitions.csv           ← 分区表

3. 第一个 FreeRTOS 任务

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

void my_task(void *pvParameters) {
    int *param = (int *)pvParameters;
    while (1) {
        printf("Task %d running on Core %d\n", *param, xPortGetCoreID());
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void app_main(void) {
    static int id1 = 1, id2 = 2;
    xTaskCreate(my_task, "task1", 2048, &id1, 2, NULL);
    xTaskCreate(my_task, "task2", 2048, &id2, 2, NULL);
    // app_main 返回后,其栈空间被回收
}

栈大小经验值

任务类型 推荐栈 (words)
简单打印 2048
WiFi 通信 4096-6144
HTTP 客户端 8192
JSON 解析 6144+

4. WiFi 连接(事件驱动)

#include "esp_wifi.h"
#include "esp_event.h"

static void wifi_event_handler(void *arg, esp_event_base_t base,
                                int32_t id, void *event_data) {
    if (id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();
    } else if (id == WIFI_EVENT_STA_DISCONNECTED) {
        esp_wifi_connect();  // 自动重连
    }
}

static void ip_event_handler(void *arg, esp_event_base_t base,
                              int32_t id, void *event_data) {
    if (id == IP_EVENT_STA_GOT_IP) {
        ip_event_got_ip_t *ev = (ip_event_got_ip_t *)event_data;
        ESP_LOGI("WiFi", "Got IP: " IPSTR, IP2STR(&ev->ip_info.ip));
    }
}

void wifi_init_sta(const char *ssid, const char *pass) {
    // 初始化网络栈 + 事件循环
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();

    // 注册事件
    esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
                               &wifi_event_handler, NULL);
    esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
                               &ip_event_handler, NULL);

    // 配置并启动 WiFi
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    wifi_config_t wifi_cfg = {0};
    strcpy((char *)wifi_cfg.sta.ssid, ssid);
    strcpy((char *)wifi_cfg.sta.password, pass);

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg));
    ESP_ERROR_CHECK(esp_wifi_start());
}

5. 组件化开发

# components/my_sensor/CMakeLists.txt
idf_component_register(
    SRCS "my_sensor.c"
    INCLUDE_DIRS "include"
    REQUIRES driver freertos esp_timer
)
// components/my_sensor/include/my_sensor.h
#pragma once
#include "esp_err.h"

typedef struct {
    float temperature;
    float humidity;
} sensor_reading_t;

esp_err_t sensor_init(void);
esp_err_t sensor_read(sensor_reading_t *out);

组件之间通过 REQUIRES 声明依赖,构建系统自动处理 include 路径和链接顺序。


6. 功耗优化

// 进入 Deep Sleep(仅 RTC + ULP 运行,~5µA)
esp_sleep_enable_timer_wakeup(60 * 1000000);  // 60 秒后唤醒
esp_deep_sleep_start();

// 唤醒后从 app_main 重新执行(类似复位)

思考题

  1. 为什么 ESP-IDF 使用事件驱动而非阻塞等待 WiFi 连接?
  2. 组件化架构相比把所有代码放 main/ 有什么优势?
  3. vTaskDelay vs vTaskDelayUntil 的区别是什么?

信息

路径
/tech-stacks/esp-idf/tutorial/入门篇:环境搭建与 FreeRTOS 多任务.md
更新时间
2026/5/31