1.3. ESP-IDF 编程基础

1.3.1. 日志系统

#include "esp_log.h"

static const char *TAG = "APP";

ESP_LOGE(TAG, "错误信息");   // Error (红色)
ESP_LOGW(TAG, "警告信息");   // Warning
ESP_LOGI(TAG, "普通信息");   // Info
ESP_LOGD(TAG, "调试信息");   // Debug (需开启)
ESP_LOGV(TAG, "冗余信息");   // Verbose

// 设置日志级别
// menuconfig → Component config → Log output → Default log verbosity

// 运行时动态调整
esp_log_level_set(TAG, ESP_LOG_VERBOSE);

1.3.2. GPIO 操作

#include "driver/gpio.h"

#define GPIO_LED    GPIO_NUM_2
#define GPIO_BUTTON GPIO_NUM_0

void gpio_init(void)
{
    // 配置输出
    gpio_reset_pin(GPIO_LED);
    gpio_set_direction(GPIO_LED, GPIO_MODE_OUTPUT);

    // 配置输入(上拉)
    gpio_reset_pin(GPIO_BUTTON);
    gpio_set_direction(GPIO_BUTTON, GPIO_MODE_INPUT);
    gpio_set_pull_mode(GPIO_BUTTON, GPIO_PULLUP_ONLY);
}

// GPIO 控制
gpio_set_level(GPIO_LED, 1);         // 高电平
gpio_set_level(GPIO_LED, 0);         // 低电平

int level = gpio_get_level(GPIO_BUTTON);  // 读取

// 翻转
gpio_set_level(GPIO_LED, !gpio_get_level(GPIO_LED));

1.3.3. NVS 非易失存储

#include "nvs_flash.h"
#include "nvs.h"

void nvs_example(void)
{
    // 初始化 NVS
    esp_err_t err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES ||
        err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        err = nvs_flash_init();
    }

    nvs_handle_t handle;
    ESP_ERROR_CHECK(nvs_open("storage", NVS_READWRITE, &handle));

    // 写入
    int32_t counter = 0;
    nvs_set_i32(handle, "counter", counter);
    nvs_commit(handle);

    // 读取
    nvs_get_i32(handle, "counter", &counter);

    nvs_close(handle);
}

1.3.4. 中断处理

#include "driver/gpio.h"

static QueueHandle_t gpio_evt_queue = NULL;

// 中断处理函数 (在 ISR 上下文中执行)
static void IRAM_ATTR gpio_isr_handler(void *arg)
{
    uint32_t gpio_num = (uint32_t)arg;
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}

void gpio_isr_init(void)
{
    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));

    // 安装 GPIO ISR 服务
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);

    // 配置中断触发
    gpio_set_intr_type(GPIO_BUTTON, GPIO_INTR_NEGEDGE);
    gpio_isr_handler_add(GPIO_BUTTON, gpio_isr_handler,
                        (void *)GPIO_BUTTON);
}

// 在任务中处理中断事件
void gpio_task(void *arg)
{
    uint32_t io_num;
    while (1) {
        if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
            ESP_LOGI(TAG, "GPIO[%d] 中断触发", io_num);
            // 处理按键逻辑
        }
    }
}

1.3.5. 定时器

#include "esp_timer.h"

void periodic_timer_callback(void *arg)
{
    ESP_LOGI(TAG, "定时器触发");
}

void timer_init(void)
{
    const esp_timer_create_args_t timer_args = {
        .callback = &periodic_timer_callback,
        .name = "periodic"
    };

    esp_timer_handle_t timer;
    ESP_ERROR_CHECK(esp_timer_create(&timer_args, &timer));

    // 每 1000 ms 触发一次
    ESP_ERROR_CHECK(esp_timer_start_periodic(timer, 1000000));
}

1.3.6. 看门狗

#include "esp_task_wdt.h"

void task_wdt_example(void)
{
    // 在主循环中喂狗
    while (1) {
        esp_task_wdt_reset();
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

警告

ESP-IDF 默认启用了中断看门狗(Interrupt WDT)和任务看门狗(Task WDT)。 如果任务阻塞超过 5 秒未让出 CPU,Task WDT 会触发复位。 可在 menuconfig 中调整超时或禁用。

1.3.7. 错误处理

esp_err_t err;

// 检查并处理错误
err = gpio_set_level(GPIO_LED, 1);
if (err != ESP_OK) {
    ESP_LOGE(TAG, "GPIO 操作失败: %s", esp_err_to_name(err));
    return err;
}

// 断言(调试用,Release 版可禁用)
ESP_ERROR_CHECK(err);        // 出错时打印并 abort
assert(ptr != NULL);          // 标准断言