一、這一篇要完成什麼?
二、系統接線
| ESP32 | 功能 | 本案接腳 |
|---|---|---|
| SDA | 資料線 | GPIO23 |
| SCL | 時脈線 | GPIO22 |
| 3.3V | 供電 | 依板卡設計 |
| GND | 共地 | 所有模組共地 |
三、第一步:啟動 I²C
#include <Wire.h>
void setup() {
Serial.begin(115200);
// SDA = GPIO23,SCL = GPIO22
Wire.begin(23, 22);
// 使用 100 kHz
Wire.setClock(100000);
}
四、第二步:使用 I²C Scanner 找出所有裝置
void scanI2C() {
for (uint8_t address = 1; address < 127; address++) {
Wire.beginTransmission(address);
uint8_t error = Wire.endTransmission();
if (error == 0) {
Serial.print("找到位址:0x");
if (address < 16) {
Serial.print("0");
}
Serial.println(address, HEX);
}
}
}
本系統實際掃描結果:
找到 0x61 → EZO DO
找到 0x63 → EZO pH
找到 0x66 → EZO RTD
找到 0x69 → EZO EC
五、第三步:向感測 IC 傳送讀取命令
Atlas EZO IC 使用字串命令。最常見的讀取命令是:
Wire.beginTransmission(address);
Wire.write("R");
Wire.endTransmission();
感測 IC 收到命令後,需要時間完成量測,因此不能立刻讀回。
六、第四步:讀回感測器資料
String readEzo(uint8_t address, unsigned long waitMs) {
String result = "";
Wire.beginTransmission(address);
Wire.write("R");
if (Wire.endTransmission() != 0) {
return "ERROR";
}
delay(waitMs);
Wire.requestFrom((int)address, 20);
while (Wire.available()) {
char c = Wire.read();
if (c == '\0') {
break;
}
result += c;
}
return result;
}
七、第五步:讀取四顆感測器
constexpr uint8_t DO_ADDR = 0x61;
constexpr uint8_t PH_ADDR = 0x63;
constexpr uint8_t RTD_ADDR = 0x66;
constexpr uint8_t EC_ADDR = 0x69;
void loop() {
String temperature = readEzo(RTD_ADDR, 1100);
String ph = readEzo(PH_ADDR, 1100);
String dissolvedOxygen = readEzo(DO_ADDR, 1100);
String conductivity = readEzo(EC_ADDR, 1100);
Serial.print("T=");
Serial.print(temperature);
Serial.print(" pH=");
Serial.print(ph);
Serial.print(" DO=");
Serial.print(dissolvedOxygen);
Serial.print(" EC=");
Serial.println(conductivity);
delay(3000);
}
八、完整入門版程式
#include <Wire.h>
constexpr uint8_t SDA_PIN = 23;
constexpr uint8_t SCL_PIN = 22;
constexpr uint8_t DO_ADDR = 0x61;
constexpr uint8_t PH_ADDR = 0x63;
constexpr uint8_t RTD_ADDR = 0x66;
constexpr uint8_t EC_ADDR = 0x69;
String readEzo(uint8_t address, unsigned long waitMs) {
String result = "";
Wire.beginTransmission(address);
Wire.write("R");
uint8_t error = Wire.endTransmission();
if (error != 0) {
return "ERROR";
}
delay(waitMs);
Wire.requestFrom((int)address, 20);
while (Wire.available()) {
char c = Wire.read();
if (c == '\0') {
break;
}
result += c;
}
return result;
}
void setup() {
Serial.begin(115200);
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000);
Serial.println("I2C Sensor Reading Start...");
}
void loop() {
String temperature = readEzo(RTD_ADDR, 1100);
String ph = readEzo(PH_ADDR, 1100);
String dissolvedOxygen = readEzo(DO_ADDR, 1100);
String conductivity = readEzo(EC_ADDR, 1100);
Serial.print("T=");
Serial.print(temperature);
Serial.print(" pH=");
Serial.print(ph);
Serial.print(" DO=");
Serial.print(dissolvedOxygen);
Serial.print(" EC=");
Serial.println(conductivity);
delay(3000);
}
九、常見錯誤如何判斷?
| 現象 | 可能原因 | 先做什麼 |
|---|---|---|
| Scanner 找不到裝置 | SDA/SCL 錯誤、供電、Enable、位址改變 | 確認接線並重新掃描 |
| endTransmission() 不等於 0 | 裝置沒有 ACK | 確認位址是否存在 |
| No Data | 等待時間不足或感測 IC 尚未完成 | 延長至 1100 ms |
| 數值明顯不合理 | 探棒未浸水、未校正、缺少溫度補償 | 確認探棒與校正狀態 |
| 只有某一顆失效 | 位址錯誤或單一模組問題 | 單獨讀取該模組 |
十、從感測器到 Dashboard
十一、本篇重點
- 先用 I²C Scanner 確認裝置是否存在。
- 每顆 IC 都使用不同 Address。
- 送出 R 指令後要等待感測器處理。
- 回傳資料通常是字串,需要解析。
- 硬體、位址、等待時間與校正,是除錯的四個重點。
結語
ESP32 讀取 I²C 感測器的核心流程並不複雜:指定 Address、送出命令、等待處理、讀回資料。真正的工程能力,來自能否用 Scanner、錯誤碼與序列監控快速定位問題。透過 Atlas Aquaponics Kit 的實際案例,大一新生可以把 IC 通訊、感測器、Arduino 程式與智慧養殖應用連結起來。


