在這個 AI 的時代,電力需求勢必持續成長。然而,其實有些支線型的小任務,並不需要動用如此高階的算力;可以透過 edge 小型化設備或裝置在本地端進行處理。
在早期的工作環境中,工程師常需要透過電腦定時查詢與監控特定的網域(domain)狀態。當時的設備與系統相對單純,網路並未像今日這樣成為各類裝置的基本能力。然而,隨著物聯網(IoT)與嵌入式系統的發展,「連網能力」已逐漸成為硬體設計中的關鍵元素。
筆者近期在接觸 Arduino Uno R3 等入門級微控制器平台時,其本體並未內建網路功能。相較之下,較新的 Arduino Uno R4 或 Arduino Uno Q 等其他系列產品,若選擇較高階的版本,通常會內建 Wi-Fi 功能。若需要使用 Wi-Fi,通常必須額外搭配 ESP-01~ESP-14 等模組;若需要有線網路,則可透過 W5100、W5200、W5500、LAN8720A 或 ENC28J60 等網路晶片,藉由 RJ-45 介面接入乙太網路。這類擴充方式雖然彈性高,但也增加了硬體整合與系統設計的複雜度。
在現今高度互聯的環境下,許多裝置若無法直接連網或與其他設備互通,往往會限制其應用場景。因此,具備內建網路功能的 MCU 逐漸受到開發者關注。理想的選擇通常需要同時兼顧價格、體積與整合度。
目前市面上能同時滿足這些條件的方案並不多,其中 ESP32 系列仍是相當常見的選擇。例如 ESP32-XN Super Mini 等小型模組,在體積與成本方面具有一定優勢。雖然其無線訊號能力相對較弱,但對於多數輕量型 IoT 應用而言,仍屬於可接受且實用的方案。
| 硬體 | 價格 $NT |
|---|---|
| ESP32-C3 Super Mini | 25~60 |
| Mini SD/Micro SD 卡模組 SPI | 5~15 |
MCU 的程式碼通常會儲存在 Flash 記憶體中,容量一般不大。如果希望在 Flash 中同時存放程式與資料,可以透過分割使用空間(Partition)來實現。
以 4MB 的 Flash 為例,從 Partition Scheme(
partitions*.csv)中可以看到,預設配置約為 1.2MB 的 APP(應用程式)空間,以及 1.5MB 的 SPIFFS 空間。1.2MB + 1.5MB = 2.7MB,其餘空間則由系統使用,例如 NVS(Non-Volatile Storage)、OTA(Over-The-Air)更新區等。如果 Flash 容量不足,也可以額外擴充 SD 卡模組。實際可用空間大小取決於 SD 卡容量,並可依需求格式化為 FAT、SPIFFS、LittleFS 等檔案系統格式。

新版的 Arduino IDE 支援 ESP32-C3 Super Mini開發板,可以選用 MakeGo 或 Nologo,主要是接腳的定義(pins_arduino.h)。
若使用 PlatformIO,則可以選擇 lolin_c3_mini、esp32-c3-devkitm-1 (二者接腳皆不同,實際使用時需依照硬體配置自行調整)。

使用 ESP32-C3 預設接腳,會用到 GPIO 4(SCK)、5(MISO)、6(MOSI)、7(SS) 及 3.3V、GND 六支接腳。

SD 卡模組的接腳都有標示,名稱只有二支腳稍微有點不同 CS(SS:7)、CLK(SCK:4),其它一樣的名稱都照接。

使用麵包板進行簡單接線後,成果大致如下。

以 ESP32-C3 Super Mini 為例,其功耗約低於 0.5W。若 24 小時全天運作,一年以 365 天計算,總時數為 8,760 小時(24 × 365)。
耗電量可估算為:
0.5W × 8,760 小時 = 4,380 Wh
由於 1 度電(1 kWh)= 1,000 Wh,因此:
4,380 Wh ÷ 1,000 = 4.38 kWh
也就是說,連續運作一年約消耗 4.38 度電。若每度電以 3 元計算,則電費約為:
4.38 × 3 ≈ 13.14 元/年。

軟體部份可以交給 Antigravity/Claude/Cursor/ChatGPT 來完成。
但有些部份需要注意事項,像是 WiFi 功率可以設小一點,不然弱雞 ESP32-C3 Super Mini 在 WiFi 高功率傳輸時會有問題。
#include <WiFi.h>
const char *ssid = "YOUR_SSID";
const char *password = "YOUR_PASSSWORD";
WiFi.mode(WIFI_STA);
//WiFi.setTxPower(WIFI_POWER_11dBm);
WiFi.setTxPower(WIFI_POWER_8_5dBm);
WiFi.setSleep(false);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}如果 WiFi 需要透過 LDAP 登入,可以使用 WPA2。但如果是在公司內部,建議最好不要直接使用,可以自行架設 WiFi AP,再連接至該 AP,並在 OpenWRT(uci) 上限制可訪問的 IP,或使用 nft/iptables/ufw/ipfw/pf 等指令進行設置。
#include "esp_wpa2.h" // Required for enterprise
#include <WiFi.h>
const char* ssid = "YOUR_ENTERPRISE_SSID";
const char* user_identity = "your_ldap_username";
const char* user_password = "your_ldap_password";
WiFi.disconnect(true);
WiFi.mode(WIFI_STA);
// Configure Enterprise Credentials
esp_wifi_sta_wpa2_ent_set_identity((uint8_t *)user_identity, strlen(user_identity));
esp_wifi_sta_wpa2_ent_set_username((uint8_t *)user_identity, strlen(user_identity));
esp_wifi_sta_wpa2_ent_set_password((uint8_t *)user_password, strlen(user_password));
esp_wifi_sta_wpa2_ent_enable();
WiFi.begin(ssid);可以使用 WiFi.hostByName 進行 IP 的查詢。
#include <WiFi.h>
const char *hostname = "www.example.com";
IPAddress ip_address;
if (WiFi.hostByName(hostname, ip_address))
{
Serial.print("Resolved IP address for ");
Serial.print(hostname);
Serial.print(" is: ");
Serial.println(ip_address);
}
else
{
Serial.println("DNS resolution failed");
}可以使用 WiFiClient 進行埠口(Port)掃描,第三個參數可以設 timeout 時間。
#include <WiFi.h>
const char *target = "192.168.1.2"; // Target IP
int startPort = 1;
int endPort = 1024;
int timeout = 200; // 200 milliseconds
WiFiClient client;
void scanPorts()
{
Serial.println("Starting scan...");
// client.setTimeout(0.5); // 0.5 seconds
for (int port = startPort; port <= endPort; port++)
{
if (client.connect(target, port, timeout))
{
Serial.print("Port open: ");
Serial.println(port);
client.stop();
}
delay(10); // small delay to avoid flooding
}
Serial.println("Scan complete.");
}連線 SPI 使用自訂接腳,如果使用 PlatformIO 開發版,但開發板沒有 Maker、Nologo 可以選,因此需要自行定義接腳。
#include <SPI.h>
#include <SD.h>
const uint8_t SD_SCK = 4;
const uint8_t SD_MISO = 5;
const uint8_t SD_MOSI = 6;
const uint8_t SD_SS = 7;
SPI.begin(SD_SCK, SD_MISO, SD_MOSI, SD_SS);
if (!SD.begin(SD_SS, SPI, 4000000)) {
Serial.println("[SD] fail");
return;
}
Serial.println("[SD] ok");資料庫設計序號、日期及 IP 位址等,可以使用 BYTE/WORD/DWORD 儲存,以節省空間並提升速度。
arduino-esp32 使用 xTaskCreateUniversal 幫 loop 建立單一任務,所以要多工需要再自己使用 xTaskCreate/xTaskCreateUniversal 建立新的任務。
其他發想與應用,不妨從有一台可連網的 286/386 電腦,導出了一些 GPIO 接腳,思考在有限硬體資源下,還能延伸出哪些實際應用場景?
- 檔案下載:只需將下載清單上傳至系統,即可自動將檔案下載並儲存至 SD 卡,之後可透過 FTP 服務進行檔案存取與下載。
- 網路巡邏:執行基本的 PING 與 Port 掃描測試,用以檢查目標主機是否在線及服務是否正常運作。
- 感測器記錄:如使用 PIR(Passive Infrared Sensor,被動式紅外線感測器)與 IP Camera 進行事件偵測與影像記錄。
- 封包轉發:輸入密碼或特定封包後,將流量轉發至隔離中的主機(如 RDP、SSH 等服務)。
首圖來源:pexel