ESP32韌體 (PlatformIO:Arduino優先,必要時用ESP-IDF) 是做什麼的?
同時載入 embedded-c-cpp 以取得編碼標準、架構、單元測試與一般除錯。此技能包含僅與ESP32相關的部分。
首先,識別專案
| 項目 | 從何處取得 | 為什麼重要 |
|---|---|---|
| 確切的晶片與模組(ESP32-WROOM-32E、ESP32-S3-WROOM-1-N16R8、ESP32-C3-MINI-1...) | platformio.ini 中的 board =、使用者、電路板上的印刷標示 | 核心、腳位、PSRAM、USB、ADC 在不同型號間差異很大 |
| 框架 | framework = arduino、espidf 或 arduino, espidf | API 與設定模型 |
| Arduino核心版本(2.x 或 3.x) | 建置紀錄中的 framework-arduinoespressif32 @ x.y.z、platform = 行,或 ESP_ARDUINO_VERSION_MAJOR | 3.x 中多個 API 已變更(LEDC、計時器)— 為錯誤版本寫的程式碼無法編譯 |
| Flash / PSRAM 大小 | 模組名稱(N16R8 = 16 MB Flash、8 MB 八位元 PSRAM)、board_upload.flash_size | 分割區、記憶體佈局 |
| 電路板接線 | 使用者/電路圖 | 避免使用 strapping 腳位、以及被Flash/PSRAM/USB/LED使用的腳位 |
若型號未知且任務涉及腳位、周邊、電源或記憶體,請在撰寫程式碼前先確認——在S3上沒問題的腳位,可能使經典ESP32無法啟動。
Arduino 還是 ESP-IDF?
| 預設使用 Arduino | 轉用 ESP-IDF(PlatformIO中:framework = espidf)或 Arduino + IDF(framework = arduino, espidf) |
|---|---|
| 原型設計與多數產品 | 需要修改 Arduino 預編譯核心未開放的 sdkconfig 選項(回滾OTA、安全開機、Flash加密、PM自動輕眠、核心傾印至Flash、FreeRTOS設定) |
| 有大量可用程式庫 | 最新的 IDF 功能、對記憶體/效能的精細控制 |
| 快速迭代 | 生產環境強化 |
ESP32 上的 Arduino 本質上已在 FreeRTOS + ESP-IDF 上運行,因此 IDF API(esp_*、xTaskCreatePinnedToCore、nvs_*、esp_sleep_*、heap_caps_*)可以直接在 Arduino 程式碼中使用。「Arduino 作為 IDF 元件」(arduino, espidf)可以同時保有 Arduino 程式庫並增加 sdkconfig 控制——這是一般的遷移路徑。
設定、platformio.ini 範本以及 2.x 與 3.x 的 API 變更請參考:references/platformio-setup.md。
避免經典 ESP32 故障的規則
- 腳位:絕不使用 Flash 腳位(ESP32上的GPIO6–11)、PSRAM腳位(WROVER上的16/17;S3八位元PSRAM模組的33–37)或使用USB時的USB腳位;將 strapping 腳位(ESP32:0、2、5、12、15)視為「謹慎使用」—— GPIO12在開機時被拉高會使3.3V Flash模組無法開機。Wi-Fi開啟時ADC2無法正常工作(經典ESP32):請使用ADC1腳位。各型號的完整腳位對照表:
references/pins-and-variants.md。 - 不要阻塞
loop()或一個任務太久:長時間的while迴圈中沒有delay()/vTaskDelay()會觸發看門狗;while (WiFi.status() != WL_CONNECTED)必須加上超時機制。 - 中斷服務常式(ISR):標記為
IRAM_ATTR,保持極簡,禁止使用Serial.print、delay、Wi-Fi呼叫;將工作交由任務處理(佇列 / 任務通知 / 旗標)。共用變數需使用volatile+ 臨界區段(portENTER_CRITICAL_ISR自旋鎖——ESP32是雙核心)。 - 任務堆疊單位為位元組:在ESP-IDF/Arduino中(不同於標準FreeRTOS的“字”)。使用
uxTaskGetStackHighWaterMark(NULL)(位元組)測量。Arduinoloop()預設堆疊大小為8 KB(使用SET_LOOP_TASK_STACK_SIZE(16 * 1024);可變更)。 - 電源供應:Wi-Fi發射時峰值電流約300–500 mA。「檢測到布朗輸出」表示電源供應有問題(穩壓器、大電容、傳輸線)——請不要停用布朗輸出檢測器。
- 堆積:避免在迴圈中進行
String拼接以及頻繁的new/malloc(導致長時間累積的記憶體碎片);應預先配置。定期檢查ESP.getFreeHeap()/heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT)。 - NVS/Flash磨損:不要在每次迴圈疊代中都寫入
Preferences/NVS;應在有變更且有限速時才寫入。 - 不可逆操作:eFuse燒錄、安全開機、生產模式的Flash加密都是永久性的。未經使用者明確確認,絕不執行
espefuse.py burn_*、在sdkconfig中啟用這些功能,或對含有資料的裝置執行erase_flash。
建置、上傳、監控(PlatformIO)
pio run -e <env> # 建置
pio run -e <env> -t upload # 透過 USB/序列埠燒錄
pio device monitor -e <env> # 序列監控(使用 monitor_speed / monitor_filters 設定)
pio run -e <env> -t upload -t monitor # 同時進行
pio run -e <env> -t uploadfs # 將 data/ 目錄上傳為 LittleFS/SPIFFS 映像
pio run -e <env> -t erase # 清除整個 Flash(會先詢問:抹除 NVS/Wi-Fi 憑證)
pio run -e <env> -t menuconfig # 僅限 ESP-IDF / arduino+espidf
pio test -e native # 主機端單元測試(請參考 embedded-c-cpp)
上傳失敗顯示「Failed to connect / Wrong boot mode detected」→ 按住 BOOT (GPIO0 / C3上為GPIO9),輕按 EN/RST,放開 BOOT;檢查傳輸線是否支援資料傳輸;關閉任何佔用該序列埠的監控程式。在上傳前確認電路板已連接且知道連接埠(pio device list)。
當發生崩潰或重啟時
可貼上的序列輸出是關鍵證據。啟用 monitor_filters = esp32_exception_decoder 和 build_type = debug,以便將回溯追蹤解碼為檔案:行號。然後參考 references/panics-and-debug.md 了解:Guru Meditation 類型(LoadProhibited、StoreProhibited、IllegalInstruction、RISC-V 上 Load/Store 存取錯誤)、堆疊金絲雀、任務/中斷看門狗、rst: 碼、布朗輸出、啟動迴圈、JTAG 除錯(S3/C3/C6 上使用 debug_tool = esp-builtin)。
參考文件
| 檔案 | 何時閱讀 |
|---|---|
references/platformio-setup.md | 建立/修正 platformio.ini、選擇 platform/core 版本、Arduino 2.x→3.x API 變更、PSRAM、USB-CDC 序列埠、分割區與檔案系統、在 PlatformIO 中使用 ESP-IDF、sdkconfig |
references/pins-and-variants.md | 選擇或審查腳位、比較 ESP32/S2/S3/C3/C6、ADC、觸控、深眠喚醒腳位 |
references/rtos-memory-power.md | 任務、核心、ISR、佇列、看門狗、堆疊/堆積/PSRAM、IRAM、深/輕睡眠、電池 |
references/panics-and-debug.md | 任何崩潰、重置、看門狗、布朗輸出、啟動迴圈、上傳失敗、JTAG 除錯、核心傾印、重置原因 |
references/connectivity-ota-security.md | Wi-Fi/BLE 穩定性、NVS/Preferences、OTA 更新與回滾、安全開機 / Flash 加密決策 |