ESP32-S3编程开发指南
基于小智AI项目实战经验,本指南详细介绍ESP32-S3的编程开发,从基础外设控制到复杂AI应用的完整开发流程。
一、开发环境选择
1.1 开发框架对比
框架 | ESP-IDF | Arduino ESP32 | PlatformIO |
---|---|---|---|
复杂度 | 高 | 低 | 中 |
性能 | 最优 | 一般 | 优 |
功能完整性 | 完整 | 受限 | 较完整 |
调试能力 | 强 | 弱 | 强 |
小智推荐 | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ |
推荐: 小智AI项目使用ESP-IDF进行核心开发,确保最佳性能和完整功能支持
1.2 ESP-IDF开发环境搭建
Windows环境配置
# 1. 下载ESP-IDF 5.3.2
# 从 https://dl.espressif.com/dl/esp-idf/ 下载
# 2. 安装到非C盘目录
# 例如: D:\Espressif\esp-idf
# 3. 配置环境变量 (自动完成)
# 双击桌面 "ESP-IDF 5.3 PowerShell"
# 4. 验证安装
idf.py --version
# 输出: ESP-IDF v5.3.2
Linux/macOS环境
# 1. 安装依赖
sudo apt-get install git wget flex bison gperf python3-pip
# 2. 克隆ESP-IDF
git clone -b v5.3.2 --recursive https://github.com/espressif/esp-idf.git
# 3. 安装工具链
cd esp-idf
./install.sh
# 4. 设置环境变量
. ./export.sh
1.3 Arduino开发环境(可选)
# Arduino IDE 2.x设置
# 文件 -> 首选项 -> 附加开发板管理器网址
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
# 工具 -> 开发板 -> 开发板管理器
# 搜索"esp32"并安装最新版本
# 选择开发板: ESP32S3 Dev Module
二、基础GPIO编程
2.1 数字IO控制
LED控制示例 (ESP-IDF)
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define LED_PIN GPIO_NUM_48 // 板载RGB LED
void app_main() {
// 配置GPIO为输出
gpio_reset_pin(LED_PIN);
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
while(1) {
gpio_set_level(LED_PIN, 1); // 点亮LED
vTaskDelay(pdMS_TO_TICKS(500));
gpio_set_level(LED_PIN, 0); // 熄灭LED
vTaskDelay(pdMS_TO_TICKS(500));
}
}
按键输入处理
#include "driver/gpio.h"
#define BUTTON_PIN GPIO_NUM_0 // Boot按钮
void button_init() {
gpio_reset_pin(BUTTON_PIN);
gpio_set_direction(BUTTON_PIN, GPIO_MODE_INPUT);
gpio_set_pull_mode(BUTTON_PIN, GPIO_PULLUP_ENABLE);
}
bool is_button_pressed() {
return gpio_get_level(BUTTON_PIN) == 0; // 按下为低电平
}
2.2 PWM调光控制
#include "driver/ledc.h"
#define PWM_CHANNEL LEDC_CHANNEL_0
#define PWM_TIMER LEDC_TIMER_0
#define PWM_PIN GPIO_NUM_2
#define PWM_FREQ 5000 // 5kHz
#define PWM_RESOLUTION LEDC_TIMER_8_BIT // 8位分辨率 (0-255)
void pwm_init() {
// 配置定时器
ledc_timer_config_t timer_config = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_num = PWM_TIMER,
.duty_resolution = PWM_RESOLUTION,
.freq_hz = PWM_FREQ,
.clk_cfg = LEDC_AUTO_CLK
};
ledc_timer_config(&timer_config);
// 配置通道
ledc_channel_config_t channel_config = {
.channel = PWM_CHANNEL,
.duty = 0,
.gpio_num = PWM_PIN,
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_sel = PWM_TIMER
};
ledc_channel_config(&channel_config);
}
void set_brightness(uint8_t brightness) {
ledc_set_duty(LEDC_LOW_SPEED_MODE, PWM_CHANNEL, brightness);
ledc_update_duty(LEDC_LOW_SPEED_MODE, PWM_CHANNEL);
}
三、音频系统编程
3.1 I2S音频接口配置
麦克风接口(INMP441)
#include "driver/i2s.h"
#define I2S_MIC_PORT I2S_NUM_0
#define I2S_MIC_WS GPIO_NUM_4
#define I2S_MIC_SCK GPIO_NUM_5
#define I2S_MIC_SD GPIO_NUM_6
#define SAMPLE_RATE 16000
#define SAMPLE_BITS I2S_BITS_PER_SAMPLE_32BIT
void i2s_mic_init() {
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_RX,
.sample_rate = SAMPLE_RATE,
.bits_per_sample = SAMPLE_BITS,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL2,
.dma_buf_count = 8,
.dma_buf_len = 1024,
};
i2s_pin_config_t pin_config = {
.ws_io_num = I2S_MIC_WS,
.ck_io_num = I2S_MIC_SCK,
.data_in_num = I2S_MIC_SD,
.data_out_num = I2S_PIN_NO_CHANGE
};
i2s_driver_install(I2S_MIC_PORT, &i2s_config, 0, NULL);
i2s_set_pin(I2S_MIC_PORT, &pin_config);
}
size_t read_microphone(int32_t* buffer, size_t samples) {
size_t bytes_read;
i2s_read(I2S_MIC_PORT, buffer, samples * sizeof(int32_t),
&bytes_read, portMAX_DELAY);
return bytes_read / sizeof(int32_t);
}
扬声器接口(MAX98357A)
#define I2S_SPK_PORT I2S_NUM_1
#define I2S_SPK_BCLK GPIO_NUM_15
#define I2S_SPK_LRC GPIO_NUM_16
#define I2S_SPK_DIN GPIO_NUM_7
void i2s_speaker_init() {
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_TX,
.sample_rate = 16000,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL2,
.dma_buf_count = 8,
.dma_buf_len = 1024,
};
i2s_pin_config_t pin_config = {
.ws_io_num = I2S_SPK_LRC,
.ck_io_num = I2S_SPK_BCLK,
.data_out_num = I2S_SPK_DIN,
.data_in_num = I2S_PIN_NO_CHANGE
};
i2s_driver_install(I2S_SPK_PORT, &i2s_config, 0, NULL);
i2s_set_pin(I2S_SPK_PORT, &pin_config);
}
void play_audio(const int16_t* audio_data, size_t samples) {
size_t bytes_written;
i2s_write(I2S_SPK_PORT, audio_data, samples * sizeof(int16_t),
&bytes_written, portMAX_DELAY);
}
3.2 音频信号处理
音量控制
#include <math.h>
void adjust_volume(int16_t* audio_buffer, size_t samples, float volume) {
// volume范围: 0.0-2.0 (0%到200%)
for(size_t i = 0; i < samples; i++) {
float sample = audio_buffer[i] * volume;
// 防止溢出
if(sample > 32767) sample = 32767;
if(sample < -32768) sample = -32768;
audio_buffer[i] = (int16_t)sample;
}
}
噪音抑制简单实现
void simple_noise_gate(int16_t* audio_buffer, size_t samples, int16_t threshold) {
for(size_t i = 0; i < samples; i++) {
if(abs(audio_buffer[i]) < threshold) {
audio_buffer[i] = 0; // 小于阈值的信号置零
}
}
}
四、网络通信编程
4.1 Wi-Fi连接管理
Wi-Fi事件处理
#include "esp_wifi.h"
#include "esp_event.h"
static EventGroupHandle_t wifi_event_group;
const int WIFI_CONNECTED_BIT = BIT0;
static void wifi_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) {
esp_wifi_connect();
ESP_LOGI("WiFi", "重新连接WiFi...");
} 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("WiFi", "连接成功,IP:" IPSTR, IP2STR(&event->ip_info.ip));
xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);
}
}
void wifi_init(const char* ssid, const char* password) {
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_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
&wifi_event_handler, NULL, NULL);
esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
&wifi_event_handler, NULL, NULL);
wifi_config_t wifi_config = {};
strcpy((char*)wifi_config.sta.ssid, ssid);
strcpy((char*)wifi_config.sta.password, password);
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());
// 等待连接
xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, false, true,
portMAX_DELAY);
}
4.2 WebSocket客户端
小智AI WebSocket通信
#include "esp_websocket_client.h"
static esp_websocket_client_handle_t client;
static void websocket_event_handler(void *handler_args, esp_event_base_t base,
int32_t event_id, void *event_data) {
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
switch (event_id) {
case WEBSOCKET_EVENT_CONNECTED:
ESP_LOGI("WebSocket", "连接成功");
break;
case WEBSOCKET_EVENT_DATA:
ESP_LOGI("WebSocket", "收到数据: %.*s", data->data_len, (char*)data->data_ptr);
// 处理AI服务器响应
handle_ai_response((char*)data->data_ptr, data->data_len);
break;
case WEBSOCKET_EVENT_ERROR:
ESP_LOGE("WebSocket", "连接错误");
break;
case WEBSOCKET_EVENT_DISCONNECTED:
ESP_LOGI("WebSocket", "断开连接");
break;
}
}
void websocket_init(const char* uri) {
esp_websocket_client_config_t websocket_cfg = {
.uri = uri,
.task_stack = 4096,
};
client = esp_websocket_client_init(&websocket_cfg);
esp_websocket_register_events(client, WEBSOCKET_EVENT_ANY,
websocket_event_handler, (void *)client);
esp_websocket_client_start(client);
}
void send_audio_data(const char* audio_base64) {
char json_message[2048];
snprintf(json_message, sizeof(json_message),
"{\"type\":\"audio\",\"data\":\"%s\"}", audio_base64);
esp_websocket_client_send_text(client, json_message, strlen(json_message),
portMAX_DELAY);
}
4.3 HTTP客户端
#include "esp_http_client.h"
esp_err_t http_post_json(const char* url, const char* json_data, char* response_buffer, size_t buffer_size) {
esp_http_client_config_t config = {
.url = url,
.method = HTTP_METHOD_POST,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_http_client_set_header(client, "Content-Type", "application/json");
esp_http_client_set_post_field(client, json_data, strlen(json_data));
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
int data_read = esp_http_client_read_response(client, response_buffer, buffer_size - 1);
response_buffer[data_read] = '\0';
}
esp_http_client_cleanup(client);
return err;
}
五、传感器接口编程
5.1 I2C通信
OLED显示屏(SSD1306)
#include "driver/i2c.h"
#define I2C_MASTER_PORT I2C_NUM_0
#define I2C_MASTER_SDA_IO GPIO_NUM_41
#define I2C_MASTER_SCL_IO GPIO_NUM_42
#define I2C_MASTER_FREQ_HZ 100000
#define SSD1306_ADDR 0x3C
void i2c_master_init() {
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
i2c_param_config(I2C_MASTER_PORT, &conf);
i2c_driver_install(I2C_MASTER_PORT, conf.mode, 0, 0, 0);
}
void ssd1306_write_command(uint8_t cmd) {
i2c_cmd_handle_t cmd_handle = i2c_cmd_link_create();
i2c_master_start(cmd_handle);
i2c_master_write_byte(cmd_handle, SSD1306_ADDR << 1 | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd_handle, 0x00, true); // 命令模式
i2c_master_write_byte(cmd_handle, cmd, true);
i2c_master_stop(cmd_handle);
i2c_master_cmd_begin(I2C_MASTER_PORT, cmd_handle, pdMS_TO_TICKS(1000));
i2c_cmd_link_delete(cmd_handle);
}
5.2 SPI通信
#include "driver/spi_master.h"
#define SPI_HOST SPI2_HOST
#define SPI_MOSI_PIN GPIO_NUM_11
#define SPI_MISO_PIN GPIO_NUM_13
#define SPI_SCLK_PIN GPIO_NUM_12
#define SPI_CS_PIN GPIO_NUM_10
spi_device_handle_t spi_device;
void spi_init() {
spi_bus_config_t bus_cfg = {
.mosi_io_num = SPI_MOSI_PIN,
.miso_io_num = SPI_MISO_PIN,
.sclk_io_num = SPI_SCLK_PIN,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 4096
};
spi_device_interface_config_t dev_cfg = {
.clock_speed_hz = 1*1000*1000, // 1MHz
.mode = 0,
.spics_io_num = SPI_CS_PIN,
.queue_size = 7,
};
ESP_ERROR_CHECK(spi_bus_initialize(SPI_HOST, &bus_cfg, SPI_DMA_CH_AUTO));
ESP_ERROR_CHECK(spi_bus_add_device(SPI_HOST, &dev_cfg, &spi_device));
}
六、多任务编程
6.1 FreeRTOS任务管理
小智AI任务架构
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
// 任务优先级定义
#define AUDIO_TASK_PRIORITY 5 // 最高优先级
#define NETWORK_TASK_PRIORITY 3
#define DISPLAY_TASK_PRIORITY 2
#define SENSOR_TASK_PRIORITY 1 // 最低优先级
// 消息队列
QueueHandle_t audio_queue;
QueueHandle_t ai_response_queue;
// 音频处理任务
void audio_task(void* parameter) {
int16_t audio_buffer[1024];
while(1) {
// 从麦克风读取音频
size_t samples = read_microphone((int32_t*)audio_buffer, 1024);
// 语音活动检测
if(is_voice_active(audio_buffer, samples)) {
// 发送到语音识别队列
xQueueSend(audio_queue, audio_buffer, 0);
}
vTaskDelay(pdMS_TO_TICKS(20)); // 20ms采样
}
}
// AI处理任务
void ai_task(void* parameter) {
int16_t audio_data[1024];
while(1) {
if(xQueueReceive(audio_queue, audio_data, portMAX_DELAY)) {
// 语音识别
char* text = speech_to_text(audio_data, 1024);
if(text) {
// 发送到AI服务器
char* response = query_ai_service(text);
if(response) {
// 发送AI响应到播放队列
xQueueSend(ai_response_queue, response, 0);
}
free(text);
free(response);
}
}
}
}
// 显示任务
void display_task(void* parameter) {
char display_text[128];
while(1) {
if(xQueueReceive(ai_response_queue, display_text, pdMS_TO_TICKS(100))) {
// 更新OLED显示
ssd1306_display_text(display_text);
// TTS语音合成并播放
synthesize_and_play(display_text);
}
// 更新WiFi信号显示
update_signal_strength();
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void create_xiaozhi_tasks() {
// 创建消息队列
audio_queue = xQueueCreate(10, sizeof(int16_t) * 1024);
ai_response_queue = xQueueCreate(5, sizeof(char) * 128);
// 创建任务
xTaskCreatePinnedToCore(audio_task, "AudioTask", 4096, NULL,
AUDIO_TASK_PRIORITY, NULL, 1); // 核心1
xTaskCreatePinnedToCore(ai_task, "AITask", 8192, NULL,
NETWORK_TASK_PRIORITY, NULL, 0); // 核心0
xTaskCreate(display_task, "DisplayTask", 4096, NULL,
DISPLAY_TASK_PRIORITY, NULL);
}
6.2 中断处理
按键中断处理
#include "driver/gpio.h"
#define BUTTON_PIN GPIO_NUM_0
#define VOLUME_UP_PIN GPIO_NUM_40
#define VOLUME_DN_PIN GPIO_NUM_39
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);
}
// GPIO事件处理任务
static void gpio_task(void* arg) {
uint32_t io_num;
while(1) {
if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
switch(io_num) {
case BUTTON_PIN:
ESP_LOGI("GPIO", "唤醒按钮按下");
handle_wake_button();
break;
case VOLUME_UP_PIN:
ESP_LOGI("GPIO", "音量+按钮按下");
volume_up();
break;
case VOLUME_DN_PIN:
ESP_LOGI("GPIO", "音量-按钮按下");
volume_down();
break;
}
}
}
}
void gpio_interrupt_init() {
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
// 配置GPIO
gpio_config_t io_conf = {
.intr_type = GPIO_INTR_NEGEDGE, // 下降沿触发
.mode = GPIO_MODE_INPUT,
.pin_bit_mask = (1ULL << BUTTON_PIN) | (1ULL << VOLUME_UP_PIN) | (1ULL << VOLUME_DN_PIN),
.pull_down_en = 0,
.pull_up_en = 1,
};
gpio_config(&io_conf);
// 安装中断服务
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
gpio_isr_handler_add(BUTTON_PIN, gpio_isr_handler, (void*) BUTTON_PIN);
gpio_isr_handler_add(VOLUME_UP_PIN, gpio_isr_handler, (void*) VOLUME_UP_PIN);
gpio_isr_handler_add(VOLUME_DN_PIN, gpio_isr_handler, (void*) VOLUME_DN_PIN);
// 创建GPIO事件处理任务
xTaskCreate(gpio_task, "gpio_task", 2048, NULL, 10, NULL);
}
七、调试和优化
7.1 日志系统
#include "esp_log.h"
// 日志标签定义
static const char* TAG_AUDIO = "AUDIO";
static const char* TAG_WIFI = "WIFI";
static const char* TAG_AI = "AI";
void debug_log_example() {
// 不同级别的日志
ESP_LOGE(TAG_AUDIO, "错误: 音频初始化失败");
ESP_LOGW(TAG_WIFI, "警告: Wi-Fi信号弱 (-75dBm)");
ESP_LOGI(TAG_AI, "信息: AI响应时间 %dms", 1250);
ESP_LOGD(TAG_AUDIO, "调试: 音频缓冲区 %d/%d", 512, 1024);
ESP_LOGV(TAG_AI, "详细: HTTP请求头完整");
// 十六进制数据打印
uint8_t buffer[16] = {0x01, 0x02, 0x03, 0x04};
ESP_LOG_BUFFER_HEX(TAG_AUDIO, buffer, 16);
}
// 运行时设置日志级别
void set_log_levels() {
esp_log_level_set("AUDIO", ESP_LOG_DEBUG);
esp_log_level_set("WIFI", ESP_LOG_INFO);
esp_log_level_set("AI", ESP_LOG_WARN);
}
7.2 性能监控
#include "esp_timer.h"
#include "esp_system.h"
// 执行时间测量
uint64_t measure_execution_time(void (*function)()) {
uint64_t start = esp_timer_get_time();
function();
uint64_t end = esp_timer_get_time();
return end - start; // 微秒
}
// 内存使用监控
void print_memory_usage() {
size_t free_heap = esp_get_free_heap_size();
size_t min_free = esp_get_minimum_free_heap_size();
ESP_LOGI("MEMORY", "空闲堆内存: %d bytes", free_heap);
ESP_LOGI("MEMORY", "最小空闲: %d bytes", min_free);
}
// CPU使用率监控
void print_task_stats() {
char buffer[1024];
vTaskGetRunTimeStats(buffer);
ESP_LOGI("TASKS", "任务运行统计:\n%s", buffer);
}
7.3 错误处理
#include "esp_err.h"
// 错误处理宏
#define CHECK_ERROR(x) do { \
esp_err_t rc = (x); \
if (rc != ESP_OK) { \
ESP_LOGE("ERROR", "错误 %s:%d %s", __FILE__, __LINE__, esp_err_to_name(rc)); \
return rc; \
} \
} while(0)
// 小智AI错误代码
typedef enum {
XIAOZHI_OK = 0,
XIAOZHI_ERR_AUDIO_INIT = -1,
XIAOZHI_ERR_WIFI_CONNECT = -2,
XIAOZHI_ERR_AI_SERVICE = -3,
XIAOZHI_ERR_MEMORY = -4,
} xiaozhi_err_t;
const char* xiaozhi_err_to_name(xiaozhi_err_t error) {
switch(error) {
case XIAOZHI_OK: return "OK";
case XIAOZHI_ERR_AUDIO_INIT: return "AUDIO_INIT_FAILED";
case XIAOZHI_ERR_WIFI_CONNECT: return "WIFI_CONNECT_FAILED";
case XIAOZHI_ERR_AI_SERVICE: return "AI_SERVICE_ERROR";
case XIAOZHI_ERR_MEMORY: return "MEMORY_ERROR";
default: return "UNKNOWN_ERROR";
}
}
八、高级特性
8.1 OTA固件升级
#include "esp_ota_ops.h"
#include "esp_http_client.h"
esp_err_t ota_update_from_url(const char* url) {
esp_http_client_config_t config = {
.url = url,
.cert_pem = NULL,
};
esp_err_t ret = esp_https_ota(&config);
if (ret == ESP_OK) {
ESP_LOGI("OTA", "OTA更新成功,重启中...");
esp_restart();
} else {
ESP_LOGE("OTA", "OTA更新失败: %s", esp_err_to_name(ret));
}
return ret;
}
8.2 低功耗模式
#include "esp_sleep.h"
void enter_deep_sleep(uint64_t sleep_time_us) {
// 配置唤醒源
esp_sleep_enable_timer_wakeup(sleep_time_us);
esp_sleep_enable_ext0_wakeup(GPIO_NUM_0, 0); // Boot按钮唤醒
ESP_LOGI("SLEEP", "进入深度睡眠 %lld秒", sleep_time_us / 1000000);
esp_deep_sleep_start();
}
void enter_light_sleep(uint32_t sleep_time_ms) {
esp_sleep_enable_timer_wakeup(sleep_time_ms * 1000);
esp_light_sleep_start();
}