Build Log · 開發歷程
用一堆零件,做出一台會拍照的迷你相機
XIAO ESP32-S3 Sense(OV2640)+ 2.4″ ILI9341 顯示器 + 搖桿,從接線、寫韌體,到一路踩坑除錯,最後做出即時取景、拍照、濾鏡、相簿與錄影都能跑的手作相機。這頁記錄整個過程,特別是那些「畫面壞掉」的失敗與解法。
MCU XIAO ESP32-S3 Sense
Camera OV2640
Display ILI9341 240×320 SPI
Input 類比搖桿
Storage microSD FAT32
Power 行動電源
01專案概覽
目標:相機畫面即時顯示在小螢幕上,並用搖桿操作拍照、濾鏡、相簿、錄影。最終完成的功能:
- Live View即時取景(RGB565 → TFT)
- Photo + EXIF拍照存 JPEG,寫入拍攝時間
- Filter濾鏡特效 + 亮度即時調整
- Gallery相簿:翻看 SD 卡照片
- Video錄影:MJPEG 存成 .avi
- WiFi Send熱點 + 網頁下載照片 + OTA
- Screen Sleep閒置關背光,搖桿喚醒
- OV2640 / OV5640相機可換,自動偵測
02手上的零件
最右邊那顆帶鏡頭的就是 XIAO ESP32-S3 Sense;中間是 2.4″ ILI9341 顯示器;還有搖桿、麵包板、杜邦線。相機透過 Sense 底板內部走線,不佔外露排針。
起點:搖桿 · 麵包板 · 2.4″ ILI9341 · 杜邦線 · XIAO ESP32-S3 Sense(含 OV2640)
03硬體:接腳不是想像的那樣
實體顯示器的絲印是 GND VCC SCL SDA RES DC CS BLK(8 pin、無 MISO)。SCL/SDA 不是 I2C,而是 SPI 的時脈/資料,只是廠商命名不同。SD 卡與顯示器共用同一組 SPI。
關鍵發現:絲印 GND VCC SCL SDA RES DC CS BLK
| 絲印 | 別名 | XIAO |
| GND | GND | GND |
| VCC | VCC | 3V3 |
| SCL | SCK 時脈 | D8 |
| SDA | MOSI 資料 | D10 |
| RES | RESET | D0 |
| DC | DC | D3 |
| CS | CS | D1 |
| BLK | 背光 | 3V3 |
04除錯歷程:六道關卡
從編譯不過,到畫面損毀、顏色錯亂——每一關的症狀、根因與解法。失敗畫面用程式重現當時的樣子(示意)。
1
Compile Error
找不到 esp32-hal-periman.h
fatal error: esp32-hal-periman.h: No such file or directory
根因
ESP32 Arduino 核心太舊(2.x);新版 Arduino_GFX 需要核心 3.x 才有的檔案。
解法
開發板管理員把 esp32 by Espressif 更新到 3.x。
2
Compile Error
'BLACK' was not declared
error: 'BLACK' was not declared in this scope
根因
新版 Arduino_GFX 移除了 BLACK/WHITE/RED 這些顏色名稱(避免與其他函式庫衝突)。
#define COL_BLACK 0x0000
#define COL_WHITE 0xFFFF
#define COL_RED 0xF800
3
Runtime · Photo
拍好的照片變迷幻塗抹
症狀
即時預覽正常,但存下的照片色彩糊成一片藍紫、有隱約輪廓。
推論
相機 RGB565 是大端序,預覽用 draw16bitBeRGBBitmap 沒事;但存檔用的 frame2jpg 需要小端序,顏色平面被打亂。
當時嘗試
存檔前先交換每個像素的位元組再編碼。(結果見下一關)

失敗畫面 · 實拍
結構還在,但色彩像融化的油畫。
4
Runtime · Photo
改了位元序,變成藍色雜訊
症狀
交換位元組後,照片反而變成整片藍色雜訊,結構全失。
結論
在這顆相機上,frame2jpg(RGB565→JPEG)這條路怎麼調都不可靠。
解法
改用相機的原生 JPEG 輸出拍照——按下時暫時切到 JPEG 模式直接存,存完切回取景。這條路和錄影相同,顏色天生正確。

失敗畫面 · 實拍
藍色雜訊,幾乎看不出內容。
5
Runtime · 關鍵根因
結構仍損毀 → 揪出真正兇手
查證
上網比對官方社群範例,發現 XIAO ESP32-S3 Sense 的相機 XCLK 時脈設 20MHz 太快,擷取時會 DMA 溢位(EV-VSYNC-OVF),產生損毀影格。預覽因一直刷新看不太出來,存成單張就定格了。
解法
把 XCLK 從 20MHz 降到 10MHz——結構立刻完全正確!
- config.xclk_freq_hz = 20000000;
+ config.xclk_freq_hz = 10000000; // XIAO Sense 必須

轉捩點
結構回來了(右半可辨識),只剩顏色問題。
6
Runtime · Color
最後一哩:紅藍互換
症狀
畫面結構完全正確,但藍色的烙鐵台顯示成紫紅色——典型紅藍通道互換。
根因
相簿/影片預覽用 TJpg 解碼,搭配 Arduino_GFX 的 draw16bitRGBBitmap 時,setSwapBytes(true) 造成位元組換位。
解法
改成 false 即可,純顯示端修正,照片本身不用重拍。
- TJpgDec.setSwapBytes(true);
+ TJpgDec.setSwapBytes(false);

失敗畫面 · 實拍
結構對了,藍↔紅整片偏紫紅。
✓
Solved
畫面正常,大功告成
結果
即時取景、拍照、濾鏡、相簿、錄影全部運作;顏色正確、影像清晰。
05功能演進(相機能動之後)
畫面穩定後,把它從「會顯示的相機」一步步養成一台真正好用的裝置。
A
Feature
拍照存 SD + EXIF 時間
做了什麼
點按搖桿拍照,存 JPEG 到 microSD(FAT32)。拍照時暫時切到相機原生 JPEG輸出(顏色正確),並在檔頭注入 EXIF 拍攝時間。
踩到的雷
起初用 frame2jpg 轉檔,照片色彩損毀;真正根因是 XCLK 20MHz(見上面第 5 關),改 10MHz + 原生 JPEG 後解決。
B
Feature
搖桿功能表:濾鏡 / 相簿 / 錄影
做了什麼
長按進功能表,搖桿上下選、點按進入。濾鏡(黑白/負片/復古…+亮度)、相簿(TJpg 解碼翻看 SD 照片)、錄影(MJPEG 存 .avi,含 AVI 檔頭修補)。
狀態機
用單一狀態機管理 LIVE / MENU / FILTER / GALLERY / VIDEO / WIFI 各模式。
C
Feature
WiFi 傳照片(熱點 + 網頁 + OTA)
做了什麼
功能表開 WiFi 熱點,手機加入後 Captive Portal 自動彈出網頁相簿,可瀏覽/下載照片;還能上傳 .bin 做 OTA 無線更新。
對時
AP 模式沒網路無法 NTP → 改由手機瀏覽器連上時把時間傳給裝置,之後拍照就有 EXIF 時間。免 App。
D
Feature
螢幕自動關閉 + 搖桿喚醒
做了什麼
閒置 30 秒自動關螢幕,按搖桿喚醒(喚醒那一下只喚醒、不誤觸)。錄影/傳輸中不關。
真正省電
把顯示器 BLK 背光線從 3V3 改接 D6(GPIO43),用程式完全關背光,而非只是畫面變黑。
E
研究後放棄
BLE 開機自動對時(iPhone CTS)
構想
開機用藍牙自動跟 iPhone 對時,讓離線也有正確時間。
查證結論
iOS 不會穩定、免 App 地對任意 BLE 裝置提供系統時間(Espressif CTS 是「裝置當時間伺服器」,社群實測需自製 App 或改用 GPS)。
決定
誠實記錄:不硬做,改用已完成的「網頁對時」。避免白工也是一種工程判斷。
F
Upgrade
升級 OV5640(500 萬畫素 + 自動對焦)
好消息
OV5640 與 OV2640 同接頭,驅動自動偵測(PID=0x5640),接線與腳位檔完全不用改。
方向
XCLK 用 20MHz(OV5640 建議值);此模組裝上後畫面上下顛倒,把 CAM_VFLIP 設 1 修正(若左右也反再把 HMIRROR 設 1)。
自動對焦
OV5640 有 VCM 對焦馬達。用函式庫 ESP32-OV5640-AF:開機 focusInit() 下載對焦韌體 → autoFocusMode() 連續對焦;拍照前等 getFWStatus()==0x10 鎖焦再擷取。硬體上 AF-VCC 要接 3.3V 馬達才會動。
Tap-to-focus
即時取景時搖桿移動準星,對焦到該點(韌體命令 0x81 + 座標 0x3024/0x3025),閒置 3 秒回全畫面連續對焦。
架構調整
切換 RGB565⇄JPEG 會重置感光元件、AF 韌體要重載,拍照因此卡頓。改成即時取景全程 JPEG(TJpg 解碼上螢幕),AF 開機載一次、拍照不再切格式,順又穩。
為何不用 YOLO
對焦是對比度問題,不是「認出物體」——OV5640 韌體靠影像銳利度自己收斂,YOLO 只給位置、幫不上對焦;何況 ESP32-S3 跑 YOLO <1fps、延遲太高。要主體對焦該用更輕的 ESP-DL 人臉偵測;物件辨識則留給「相簿離線標記」。
實測
已上板驗證:方向修正、連續對焦、拍照鎖焦、搖桿 tap-to-focus 準星均正常運作(觸控對焦區域座標視模組可再微調)。
06學到的重點
- 版本相依函式庫與核心版本要對上(periman.h、顏色常數都是版本坑)
- 位元序相機 RGB565 是大端序;顯示、編碼、解碼各有各的位元序設定
- XCLK 10MHzXIAO Sense 擷取(尤其 JPEG)務必降到 10MHz
- 原生 JPEG拍照/錄影走相機原生 JPEG,避開 RGB565 轉檔的坑
- 共用 SPI顯示器與 SD 卡共用匯流排,讀檔要讀滿、注意時序
- 先分而治之把「檔案壞」與「顯示壞」分開驗證(拔卡到電腦看),少走冤枉路
- 知道何時收手BLE 自動對時查證後不可行就放棄,避免白工也是工程判斷
- 省電看硬體要真正關背光,得能用 GPIO 控制 BLK 腳,不只是畫面變黑
- 自動偵測好處多換 OV5640 免改腳位,只需調 XCLK 與畫面方向
關於失敗畫面:本頁的損毀/雜訊/偏色畫面是用程式重現當時的樣子(示意),方便理解各關卡的症狀差異。若要換成實機拍下的真實照片,把照片放進 images/ 後替換對應區塊即可。