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;
}
}
四、網路通訊程式設計
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);
}
五、除錯和最佳化
5.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);
}
5.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);
}