神攝手 ESP32CAM × Arduino × Arduino IDE
這一頁只講 Arduino IDE(C++):在 Arduino/ESP32/UNO R4(I2C) 上用它讀神攝手 ESP32CAM的安裝步驟、每個值怎麼讀、能用的命令與範例。要換產品、主機或軟體,用上方的切換列。
要買哪個版本、怎麼接
買 通用 I2C 版(4 pin:5V、GND、SDA、SCL),出廠 I2C 位址 0x4C。UNO/Nano 接 A4(SDA)、A5(SCL);UNO R4、ESP32 接板子上標 SDA/SCL 的腳。
安裝與設定
你需要
- 感應器:神攝手 ESP32CAM(通用 I2C 版),附 4 條線的連接線。SPIKE 版、EV3 版的韌體不同,不能直接接 Arduino。
- 板子:Arduino UNO(或 Nano)最常見;Mega、UNO R4、ESP32 也可以。
- USB 線:UNO 用方頭(USB-B)線;Nano 依板子是 Mini USB 或 Type-C。要能傳資料的線,有些便宜的線只能充電。
- 電腦:Windows、Mac 都可以。
- 軟體:Arduino IDE(免費)。
安裝軟體
提示
第一次用 Arduino?先看 第一次用 Arduino:從零開始,15 分鐘學會安裝軟體、上傳程式、看結果。
- 到官網下載:arduino.cc/en/software,選你的電腦系統(Windows 選 Windows Win 10 and newer)。
- 下載頁會問要不要捐款,點 JUST DOWNLOAD 就能直接下載。
- 安裝時一路按「下一步」,中途問要不要安裝驅動程式,全部按「安裝」。
- 想改成中文介面:File → Preferences → Language 選「中文(繁體)」,按 OK 後重開。
- 用 ESP32 的話,要先安裝 ESP32 的板子套件。
讓軟體認得感應器
不用裝函式庫。 每支範例都是單一 .ino 檔,讀寫感應器的小幫手函式(例如 mbcReadMany)已經寫在檔案裡,複製貼上就能用。
- 用 USB 線把 Arduino 接上電腦,在 IDE 上方的下拉選單選你的板子(例如 Arduino Uno),右邊會顯示 COM 幾(Mac 是
/dev/cu.usb…)。 - 在範例頁按程式框右上角的「複製」,或下載
.ino檔。 - 檔案 → 新增,把原本的內容全部刪掉,貼上程式。
- 按左上角的 →(上傳),下方出現「上傳完成」就成功了。
感應器走 I2C,出廠位址是 0x4C;協議細節看 MBC 通用 I2C 協議。
接線
MBC 通用 I2C 版只用 4 條線:兩條電源(VCC、GND)、兩條訊號(SDA、SCL)。
| 感應器 | UNO/Nano | Mega | UNO R4 | ESP32 |
|---|---|---|---|---|
| VCC | 5V | 5V | 5V | 5V(VIN) |
| GND | GND | GND | GND | GND |
| SDA | A4 | 20 | A4 | GPIO 21 |
| SCL | A5 | 21 | A5 | GPIO 22 |
重要
- 接線前先拔掉 USB 線,接好、檢查一次再插回去。
- 感應器線標 TX/RX 的話:TX 接 SCL、RX 接 SDA。
- 每個產品的接頭排列不太一樣,以感應器板子上印的字為準。
- UNO 靠近 USB 接頭那一側最上面兩個沒寫數字的腳也標著 SDA、SCL,跟 A4、A5 是同一組。
先在感應器上設定顏色
神攝手的設定都在感應器背面的彩色螢幕與按鍵上完成,不用電腦:短按側鍵進入設定選單,用左/右鍵選擇、確認鍵進入;設定好的顏色與鏡頭參數會自動保存,下次開機直接使用。
注意
神攝手的 I2C(位址
0x4C)用 SDA = IO3、SCL = IO1,接上 I2C 後 USB 序列監看就沒了;硬體已確認可做,上機待測。神攝手本身是 ESP32,不能時脈延展,讀值前一定要等 2 ms(範例小幫手已經有delay(2))。
第一次測試
- 上傳本頁的範例 1。
- 按 IDE 右上角的 放大鏡(序列埠監控窗),速度選 115200 baud(所有 MBC 範例都用這個速度)。
- 看到範例印出的文字和數字一直更新,就是接好了。
常見問題
- 一直出現「找不到感應器」? 檢查 SDA、SCL 有沒有接反、VCC 和 GND 有沒有插緊;確認是通用 I2C 版。
- 讀到
-32768? 代表讀不到感應器(接線鬆了、位址錯了),或那個編號是空號。大部分的值不會是這個數字;用滿 16 位元的值例外(掌控者十六鍵只按 ■、循行者 16 路壓線位元圖只有第 16 路壓線、陀螺儀的原始值),真的會讀到 -32768,這種值要用mbcReadMany回傳的 true/false 判斷有沒有讀到。 - 序列埠監控窗一堆亂碼? 速度沒選 115200。
- 下拉選單找不到板子? USB 線只能充電 → 換一條;板子是 CH340 相容板 → 先安裝 CH340 驅動程式,再拔插 USB。
- ESP32 讀不到或值怪怪的? 有些 MBC 產品本身是 ESP32 做的,不能時脈延展,寫完編號要等 2 ms 再讀;範例小幫手裡的
delay(2)不要刪。
讀值(學校版)
下面的寫法都用到 mbcRead() 小幫手:展開「小幫手程式」,第 ① 段(2 行)放在程式最上面、第 ② 段貼在程式最下面,位址 0x4C。
小幫手程式(① 放最上面、② 貼最下面)
① 放在程式最上面(setup() 前面)
#include <Wire.h>
const int16_t MBC_NONE = -32768; // 讀不到/空號② 貼在程式最下面(loop() 後面)
// 從編號 id 起連續讀 k 個值(k ≤ 16),int16 小端
bool mbcReadMany(uint8_t addr, uint8_t id, uint8_t k, int16_t *out) {
Wire.beginTransmission(addr);
Wire.write(id);
if (Wire.endTransmission() != 0) return false;
delay(2); // 一律等 2 ms(ESP32 產品不能時脈延展)
if (Wire.requestFrom(addr, (uint8_t)(k * 2)) != (uint8_t)(k * 2)) return false;
for (uint8_t i = 0; i < k; i++) {
uint8_t lo = Wire.read(), hi = Wire.read();
out[i] = (int16_t)(lo | (hi << 8));
}
return true;
}
// 讀 1 個值;讀不到回 MBC_NONE
int16_t mbcRead(uint8_t addr, uint8_t id) {
int16_t v;
return mbcReadMany(addr, id, 1, &v) ? v : MBC_NONE;
}
// 寫入命令(0~3 個參數)
// 2 個參數、第 1 個是 0 時要寫 (uint8_t)0,例如 mbcWrite(ADDR, 0xF8, (uint8_t)0x00, 0xFF),不然會跟下面陣列版的 mbcWrite 分不清而編不過
void mbcWrite(uint8_t addr, uint8_t id, const uint8_t *args, uint8_t n) {
Wire.beginTransmission(addr);
Wire.write(id);
for (uint8_t i = 0; i < n; i++) Wire.write(args[i]);
Wire.endTransmission();
}
void mbcWrite(uint8_t addr, uint8_t id) { mbcWrite(addr, id, nullptr, 0); }
void mbcWrite(uint8_t addr, uint8_t id, uint8_t a) { mbcWrite(addr, id, &a, 1); }
void mbcWrite(uint8_t addr, uint8_t id, uint8_t a, uint8_t b) { uint8_t p[2] = {a, b}; mbcWrite(addr, id, p, 2); }
void mbcWrite(uint8_t addr, uint8_t id, uint8_t a, uint8_t b, uint8_t c) { uint8_t p[3] = {a, b, c}; mbcWrite(addr, id, p, 3); }
// 等模組就緒(F3 bit0 = 1),最多 ms 毫秒
bool mbcWaitReady(uint8_t addr, unsigned long ms) {
unsigned long t0 = millis();
while (millis() - t0 < ms) {
int16_t st = mbcRead(addr, 0xF3);
if (st != MBC_NONE && (st & 1)) return true;
delay(20);
}
return false;
}色相 hue
| 範圍 | 意思 |
|---|---|
-1~100 |
S1 取樣點色相 0 |
int16_t hue = mbcRead(0x4C, 0x01); // 色相:-1~100讀不到(沒接、位址錯)時回 MBC_NONE(−32768)。
飽和度 saturation
| 範圍 | 意思 |
|---|---|
0~100 |
S1 取樣點飽和度 0~100 |
int16_t saturation = mbcRead(0x4C, 0x02); // 飽和度:0~100讀不到(沒接、位址錯)時回 MBC_NONE(−32768)。
明度 value
| 範圍 | 意思 |
|---|---|
0~100 |
S1 取樣點明度 0~100 |
int16_t value = mbcRead(0x4C, 0x03); // 明度:0~100讀不到(沒接、位址錯)時回 MBC_NONE(−32768)。
動態 motion
| 範圍 | 意思 |
|---|---|
0~100 |
全畫面動態量 0~100(畫面變化越多越大) |
int16_t motion = mbcRead(0x4C, 0x04); // 動態:0~100讀不到(沒接、位址錯)時回 MBC_NONE(−32768)。
色塊 X blobX
| 範圍 | 意思 |
|---|---|
0~100 |
色塊中心 X 0~100,左 = 0;沒找到 = 0 |
int16_t blobX = mbcRead(0x4C, 0x05); // 色塊 X:0~100讀不到(沒接、位址錯)時回 MBC_NONE(−32768)。
色塊 Y blobY
| 範圍 | 意思 |
|---|---|
0~75 |
色塊中心 Y 0~75,下 = 0、上 = 75;沒找到 = 0 |
int16_t blobY = mbcRead(0x4C, 0x06); // 色塊 Y:0~75讀不到(沒接、位址錯)時回 MBC_NONE(−32768)。
色塊寬 blobW
| 範圍 | 意思 |
|---|---|
0~100 |
色塊寬 W 0~100;0 = 沒找到 |
int16_t blobW = mbcRead(0x4C, 0x07); // 色塊寬:0~100讀不到(沒接、位址錯)時回 MBC_NONE(−32768)。
色塊高 blobH
| 範圍 | 意思 |
|---|---|
0~75 |
色塊高 H 0~75;沒找到 = 0 |
int16_t blobH = mbcRead(0x4C, 0x08); // 色塊高:0~75讀不到(沒接、位址錯)時回 MBC_NONE(−32768)。
色塊面積 blobArea
| 範圍 | 意思 |
|---|---|
0~7500 |
W×H(0~7500);沒找到 = 0 |
int16_t blobArea = mbcRead(0x4C, 0x09); // 色塊面積:0~7500讀不到(沒接、位址錯)時回 MBC_NONE(−32768)。
有找到色塊 found
| 範圍 | 意思 |
|---|---|
0~1 |
1 = 畫面裡有目標色的色塊(W > 0),0 = 沒有 |
int16_t found = mbcRead(0x4C, 0x0A); // 有找到色塊:0~1讀不到(沒接、位址錯)時回 MBC_NONE(−32768)。
讀值(WRO 版)
下面的寫法都用到 mbcRead() 小幫手:展開「小幫手程式」,第 ① 段(2 行)放在程式最上面、第 ② 段貼在程式最下面,位址 0x4C。
小幫手程式(① 放最上面、② 貼最下面)
① 放在程式最上面(setup() 前面)
#include <Wire.h>
const int16_t MBC_NONE = -32768; // 讀不到/空號② 貼在程式最下面(loop() 後面)
// 從編號 id 起連續讀 k 個值(k ≤ 16),int16 小端
bool mbcReadMany(uint8_t addr, uint8_t id, uint8_t k, int16_t *out) {
Wire.beginTransmission(addr);
Wire.write(id);
if (Wire.endTransmission() != 0) return false;
delay(2); // 一律等 2 ms(ESP32 產品不能時脈延展)
if (Wire.requestFrom(addr, (uint8_t)(k * 2)) != (uint8_t)(k * 2)) return false;
for (uint8_t i = 0; i < k; i++) {
uint8_t lo = Wire.read(), hi = Wire.read();
out[i] = (int16_t)(lo | (hi << 8));
}
return true;
}
// 讀 1 個值;讀不到回 MBC_NONE
int16_t mbcRead(uint8_t addr, uint8_t id) {
int16_t v;
return mbcReadMany(addr, id, 1, &v) ? v : MBC_NONE;
}
// 寫入命令(0~3 個參數)
// 2 個參數、第 1 個是 0 時要寫 (uint8_t)0,例如 mbcWrite(ADDR, 0xF8, (uint8_t)0x00, 0xFF),不然會跟下面陣列版的 mbcWrite 分不清而編不過
void mbcWrite(uint8_t addr, uint8_t id, const uint8_t *args, uint8_t n) {
Wire.beginTransmission(addr);
Wire.write(id);
for (uint8_t i = 0; i < n; i++) Wire.write(args[i]);
Wire.endTransmission();
}
void mbcWrite(uint8_t addr, uint8_t id) { mbcWrite(addr, id, nullptr, 0); }
void mbcWrite(uint8_t addr, uint8_t id, uint8_t a) { mbcWrite(addr, id, &a, 1); }
void mbcWrite(uint8_t addr, uint8_t id, uint8_t a, uint8_t b) { uint8_t p[2] = {a, b}; mbcWrite(addr, id, p, 2); }
void mbcWrite(uint8_t addr, uint8_t id, uint8_t a, uint8_t b, uint8_t c) { uint8_t p[3] = {a, b, c}; mbcWrite(addr, id, p, 3); }
// 等模組就緒(F3 bit0 = 1),最多 ms 毫秒
bool mbcWaitReady(uint8_t addr, unsigned long ms) {
unsigned long t0 = millis();
while (millis() - t0 < ms) {
int16_t st = mbcRead(addr, 0xF3);
if (st != MBC_NONE && (st & 1)) return true;
delay(20);
}
return false;
}色點 1~12 顏色碼 spotColor
| 範圍 | 意思 |
|---|---|
0~255 |
10 |
int16_t spotColor[12];
mbcReadMany(0x4C, 0x10, 12, spotColor); // 色點 1~12 顏色碼:0~255
for (uint8_t i = 0; i < 12; i++) { Serial.print(spotColor[i]); Serial.print(' '); }
Serial.println();編號 0x10~0x1B 連號,一次 mbcReadMany 讀完保證同一瞬間。
色點 1~12 鮮豔度 spotVivid
| 範圍 | 意思 |
|---|---|
0~255 |
20 |
int16_t spotVivid[12];
mbcReadMany(0x4C, 0x20, 12, spotVivid); // 色點 1~12 鮮豔度:0~255
for (uint8_t i = 0; i < 12; i++) { Serial.print(spotVivid[i]); Serial.print(' '); }
Serial.println();編號 0x20~0x2B 連號,一次 mbcReadMany 讀完保證同一瞬間。
範例(學校版)
警告
硬體已確認可以接 Arduino,I2C 版韌體還在上機測試中。 這一頁的接線與範例是照 MBC I2C 協議 v2 的定案寫好、三種板子都編譯過的;實機測完會把這段提示拿掉。想現在就用神攝手,請先用 SPIKE、EV3 版。
這一頁有 6 個範例:學校版(取色、色塊追蹤)5 支,WRO 版(12 個色點)1 支。每個範例都可以直接複製、上傳,程式裡每一行都有中文說明。完全沒寫過程式也沒關係,照順序一個一個做就好。
提示
第一次用 Arduino?先看 🔰 第一次用 Arduino:從零開始,15 分鐘學會安裝軟體、上傳程式、看結果。
接線(所有範例都一樣)
| 神攝手(產品端) | Arduino UNO / Nano | Arduino UNO R4 | ESP32 |
|---|---|---|---|
| 5V | 5V | 5V | 5V(VIN) |
| GND | GND | GND | GND |
| IO3(= SDA) | A4 | SDA | GPIO 21 |
| IO1(= SCL) | A5 | SCL | GPIO 22 |
產品端 IO3/IO1 是什麼? 神攝手的主機接頭用的是 ESP32 的 IO3 和 IO1 兩支腳(跟 EV3/SPIKE 串口版用同一組線)。燒成 I2C 版韌體時,IO3 當 SDA、IO1 當 SCL,I2C 位址 0x4C。所以:
重要
- 神攝手要燒 I2C 版韌體。EV3 版、SPIKE 版用同一組腳走串口,只能二選一,不能同時支援。
- 硬體接線已確認;I2C 版韌體上機待測(見頁首提示)。
- 神攝手要先在背面螢幕設定取樣點 S1 和要追的顏色(設定存在機身),Arduino 只負責讀結果。
- 神攝手是 ESP32 做的從機,小幫手裡的
delay(2)不能刪。- 開機要幾秒(鏡頭初始化+螢幕),範例最多等 8 秒。接線前先拔掉 USB 線。
指令總覽(這台神攝手能讀到什麼)
神攝手就像一排有編號的「格子」,每一格放一個數字(int16)。寫 [編號] 再讀 2 個 byte 就拿到一個值;連續的編號可以一次讀一串(最多 16 個)。範例裡的 mbcRead(ADDR, 編號)、mbcReadMany(ADDR, 起始編號, 幾個, 陣列) 就是在做這件事。學校版用 010A,WRO 版(HUE12)用 101B、20~`2B;F0` 的高 byte 告訴你是哪一版。
| 編號 | 名稱 | 範圍/單位 | 說明 | 出現在 |
|---|---|---|---|---|
01 |
色相(學校版) | −1~100 | 取樣點 S1 的色相:紅 0/100、黃約 17、綠約 33、青約 50、藍約 67、紫約 83;−1 = 黑白灰(飽和度 < 10);S1 沒設定 = 0 | 範例 2、3 |
02 |
飽和度 | 0~100 | S1 的飽和度,越高越鮮豔 | 範例 2、3 |
03 |
明度 | 0~100 | S1 的明度,越高越亮 | 範例 2、3 |
04 |
動態 | 0~100 | 全畫面動態量,畫面變化越多越大 | 範例 2 |
05 |
色塊 X | 0~100 | 色塊中心 X,左 = 0;沒找到 = 0 | 範例 2、4、5 |
06 |
色塊 Y | 0~75 | 色塊中心 Y,下 = 0、上 = 75;沒找到 = 0 | 範例 2、4 |
07 |
色塊寬 | 0~100 | 0 = 沒找到 | 範例 2、4 |
08 |
色塊高 | 0~75 | 沒找到 = 0 | 範例 2、4 |
09 |
色塊面積 | 0~7500 | 寬 × 高 | 範例 2、4 |
0A |
有找到色塊 | 0~1 | 1 = 畫面裡有目標色的色塊(寬 > 0) | 範例 2、4、5 |
10~`1B` |
色點 S1~S12 顏色碼(WRO 版) | 0~255 | 0 |
範例 6 |
20~`2B` |
色點 S1~S12 鮮豔度(WRO 版) | 0~255 | 黑 = 1、白/彩色 = 實際飽和度;色點沒開 = 0 | 範例 6 |
F0 |
裝置 ID | — | 低 byte = 出廠位址 0x4C、高 byte = 1 學校版/2 WRO 版 | 範例 1、6 |
F1 |
協議版本 | 2 | 這份範例照版本 2 寫 | 範例 1 |
F2 |
韌體版本 | 主×100+次 | 例如 102 = 1.2 | 範例 1 |
F3 |
狀態旗標 | bit | b0 就緒、b1 有找到色塊(學校版;WRO 版恆 0) | 範例 1、所有範例的 mbcWaitReady |
F5 |
開機秒數 | 0~32767 | 數字變小 = 重開機過 | 範例 1 |
注意
神攝手沒有寫入命令(目標顏色、補光燈都在機身螢幕設定)。沒有的編號(學校版讀
102B、WRO 版讀010A)會讀到-32768(範例裡叫MBC_NONE),讀不到神攝手也是-32768。完整規格見 MBC 通用 I2C 協議 v2。
範例總覽
| # | 範例 | 版本 | 學會什麼 | 另外要準備 |
|---|---|---|---|---|
| 1 | 跟神攝手打招呼 | 兩版 | 確認接線正確、讀身分證、分辨學校版/WRO 版 | 無 |
| 2 | 把所有的值一次印出來 | 學校版 | 看懂 01~0A 每個編號放什麼 | 幾張彩色紙 |
| 3 | 這是什麼顏色? | 學校版 | 色相/飽和/明度 → 顏色名字 | 幾張彩色紙 |
| 4 | 色塊在哪裡? | 學校版 | X/Y/寬/高/面積/有找到 | 目標顏色的物品 |
| 5 | 伺服馬達跟著色塊轉(進階) | 學校版 | 用 X 控制角度;UNO 與 ESP32 兩種伺服寫法 | SG90 伺服馬達 |
| 6 | WRO 版:12 個色點一次看 | WRO 版 | 一次讀 12 個顏色碼+12 個鮮豔度 | WRO 場地 |
提示
建議順序:1 → 2 → 3 → 4 → 5。 燒 WRO 版韌體的神攝手跑 1 → 6。
1️⃣ 跟神攝手打招呼
做什麼: 確認神攝手接好了,印出它的「身分證」(出廠位址、學校版還是 WRO 版、協議版本、韌體版本)和狀態旗標。每次接新的神攝手,都先跑這一個。
步驟:
- 照上面的表格接好 4 條線,USB 插上電腦(神攝手螢幕會亮)。
- 複製下面的程式,貼到 Arduino IDE,按 → 上傳。
- 打開序列埠監控窗(右上角放大鏡),速度選 115200。
/*
* 神攝手 ESP32CAM × Arduino 範例 1:跟神攝手打招呼
* ------------------------------------------------------------
* 這個範例會做什麼?
* 確認神攝手有沒有接好。接好了,會印出它的身分證(裝置 ID、是學校版還是 WRO 版、
* 協議版本、韌體版本)和狀態旗標 F3(就緒?有沒有找到色塊?)。
* 每次接新的神攝手,都建議先跑這個範例。
*
* 你需要:神攝手 ESP32CAM(I2C 版韌體)、Arduino UNO/Nano/UNO R4 或 ESP32。
*
* 接線:
* 神攝手 5V → 5V 神攝手 GND → GND
* 神攝手 IO3(SDA)→ A4 神攝手 IO1(SCL)→ A5 (UNO / Nano)
* UNO R4 接 SDA / SCL 腳;ESP32 接 GPIO 21 (SDA) / GPIO 22 (SCL)
* ※ 硬體已確認可接,I2C 版韌體上機待測。IO1/IO3 跟 EV3/SPIKE 串口版共用,韌體二選一。
*
* 怎麼看結果:
* 上傳後,打開「序列埠監控窗」,速度選 115200。
*
* 小提醒:
* 神攝手開機要幾秒(鏡頭初始化+螢幕),範例最多等 8 秒。
* 這支範例學校版、WRO 版都能跑;F0 的高 byte 會告訴你是哪一版。
*/
#include <Wire.h> // Arduino 內建的 I2C 通訊工具,用來跟神攝手講話
const uint8_t ADDR = 0x4C; // 神攝手的「門牌號碼」(I2C 位址),學校版與 WRO 版都是 0x4C
const int16_t MBC_NONE = -32768; // 「沒有這個值/讀不到」的代號
// 小幫手的宣告(實作在檔案最下面);mbcWrite 的參數可以省略
bool mbcWrite(uint8_t addr, uint8_t id, const uint8_t *p = NULL, uint8_t n = 0);
void setup() {
Serial.begin(115200); // 打開跟電腦講話的通道,速度 115200
mbcBegin(); // 準備好 I2C 通訊(小幫手在檔案最下面)
delay(500);
Serial.println(F("=== 神攝手 ESP32CAM:跟神攝手打招呼 ==="));
// 等神攝手說「我準備好了」(狀態旗標 F3 的 bit0),最多等 8 秒
if (!mbcWaitReady(ADDR, 8000)) {
Serial.println(F("找不到神攝手!請檢查:"));
Serial.println(F(" 1. IO3(SDA)接 A4、IO1(SCL)接 A5(ESP32:21 / 22),有沒有接反?"));
Serial.println(F(" 2. 5V、GND 有沒有插緊?神攝手的螢幕有沒有亮?"));
Serial.println(F(" 3. 神攝手燒的是「I2C 版」韌體嗎?(EV3 版、SPIKE 版用同一組腳,不能接 Arduino)"));
while (true) {} // 找不到就停在這裡
}
// ---- F0 裝置 ID:低 byte = 出廠位址(0x4C)、高 byte = 版本(1 = 學校版、2 = WRO 版) ----
int16_t id = mbcRead(ADDR, 0xF0);
uint8_t edition = (uint8_t)((id >> 8) & 0xFF);
Serial.print(F("找到了!出廠位址 0x"));
Serial.println((uint8_t)(id & 0xFF), HEX); // 應該是 4C
Serial.print(F("版本:"));
if (edition == 1) Serial.println(F("學校版(取色+色塊追蹤+動態偵測,編號 01~0A)"));
else if (edition == 2) Serial.println(F("WRO 版 HUE12(12 個色點,編號 10~1B、20~2B)"));
else Serial.println(F("不認識的版本"));
// ---- F1 協議版本(這份範例是照版本 2 寫的) ----
Serial.print(F("協議版本:"));
Serial.println(mbcRead(ADDR, 0xF1));
// ---- F2 韌體版本:主版×100 + 次版(例如 102 = 1.2) ----
int16_t fw = mbcRead(ADDR, 0xF2);
Serial.print(F("韌體版本:"));
Serial.print(fw / 100);
Serial.print('.');
Serial.println(fw % 100);
// ---- F3 狀態旗標 ----
int16_t st = mbcRead(ADDR, 0xF3);
Serial.println(F("目前狀態:"));
Serial.print(F(" bit0 就緒 ")); Serial.println((st & 0x01) ? F("是") : F("否"));
Serial.print(F(" bit1 有找到色塊 ")); Serial.println((st & 0x02) ? F("是") : F("否(學校版才有;WRO 版恆 0)"));
// ---- F5 開機秒數 ----
Serial.print(F("開機秒數:"));
Serial.println(mbcRead(ADDR, 0xF5));
Serial.println(F("接線正確,可以開始玩其他範例了!"));
}
void loop() {
// 這個範例只要在開機時檢查一次,所以 loop 裡什麼都不做
}
// ============================================================
// 以下是「通訊小幫手」(MBC I2C 協議 v2):負責跟感應器講話。
// 所有 MBC 範例都一樣,不用改,也不用看懂。
// 讀法:寫 [編號] → 等 2 ms → 讀 2×k 個 byte = 從該編號起連續 k 個 int16(小端)
// 沒有這個編號(空號)時感應器回 -32768(MBC_NONE)
// ============================================================
// 開始 I2C 通訊(setup 裡呼叫一次)
// UNO/Nano:SDA=A4、SCL=A5;UNO R4:SDA/SCL 腳;ESP32:預設 GPIO21(SDA)/22(SCL)
void mbcBegin() {
Wire.begin();
#if defined(WIRE_HAS_TIMEOUT)
Wire.setWireTimeout(25000, true); // 線沒接好時,不會讓 Arduino 當機卡住
#endif
}
// 從位址 addr 的感應器,由編號 id 起連續讀 k 個值(1~16)放進 out。全部讀到回傳 true
bool mbcReadMany(uint8_t addr, uint8_t id, uint8_t k, int16_t *out) {
if (k < 1) k = 1;
if (k > 16) k = 16; // 一次最多 16 個值(32 byte,Wire 緩衝區上限)
Wire.beginTransmission(addr);
Wire.write(id); // 告訴感應器:我要從第 id 號開始讀
if (Wire.endTransmission() != 0) return false;
delay(2); // 等 2 ms 再讀(ESP32 型的產品一定要等)
uint8_t n = (uint8_t)(2 * k);
if (Wire.requestFrom(addr, n) != n) return false;
for (uint8_t i = 0; i < k; i++) {
uint16_t lo = Wire.read(); // 先來的是低位元組
uint16_t hi = Wire.read(); // 再來高位元組(小端)
out[i] = (int16_t)(uint16_t)(lo | (hi << 8));
}
return true;
}
// 讀一個值。讀不到(沒接好)回傳 MBC_NONE
int16_t mbcRead(uint8_t addr, uint8_t id) {
int16_t v;
return mbcReadMany(addr, id, 1, &v) ? v : MBC_NONE;
}
// 送命令:寫 [id][參數…]。p 指向參數、n = 參數幾個 byte(沒參數就不填)。成功回傳 true
bool mbcWrite(uint8_t addr, uint8_t id, const uint8_t *p, uint8_t n) {
Wire.beginTransmission(addr);
Wire.write(id);
for (uint8_t i = 0; i < n; i++) Wire.write(p[i]);
return Wire.endTransmission() == 0;
}
// 等感應器準備好(狀態旗標 F3 的 bit0 = 1),最多等 ms 毫秒。準備好了回傳 true
bool mbcWaitReady(uint8_t addr, uint16_t ms) {
unsigned long start = millis();
while (millis() - start < ms) {
int16_t f = mbcRead(addr, 0xF3);
if (f != MBC_NONE && (f & 1)) return true;
delay(50);
}
return false;
}你會看到:(學校版,鏡頭前有目標色)
=== 神攝手 ESP32CAM:跟神攝手打招呼 ===
找到了!出廠位址 0x4C
版本:學校版(取色+色塊追蹤+動態偵測,編號 01~0A)
協議版本:2
韌體版本:1.2
目前狀態:
bit0 就緒 是
bit1 有找到色塊 是
開機秒數:6
接線正確,可以開始玩其他範例了!注意
「通訊小幫手」是什麼? 每個範例最下面都有一段一模一樣的程式,負責跟神攝手「講話」。你不用改它,也不用看懂。只要記得:
mbcRead(ADDR, 編號):讀一個值(讀不到回-32768)mbcReadMany(ADDR, 起始編號, 幾個, 陣列):從某個編號起一次讀一串(最多 16 個,同一瞬間的值)mbcWaitReady(ADDR, 毫秒):等神攝手開機好小幫手裡的
delay(2)(寫完編號等 2 毫秒再讀)不能刪:神攝手是 ESP32 做的從機,需要這 2 毫秒準備資料。
2️⃣ 把所有的值一次印出來(學校版)
做什麼: 每 0.5 秒把學校版值表 01~`0A` 的 10 個值一次讀出來印出來。拿不同顏色的東西到鏡頭前,看哪一行在變。
/*
* 神攝手 ESP32CAM × Arduino 範例 2:把所有的值一次印出來(學校版)
* ------------------------------------------------------------
* 這個範例會做什麼?
* 每 0.5 秒把學校版「值表」01~0A 的 10 個值一次讀出來印出來:
* 01 色相、02 飽和度、03 明度、04 動態(取樣點 S1 的顏色+全畫面動態量)
* 05 色塊 X、06 色塊 Y、07 寬、08 高、09 面積、0A 有找到
* 連續的編號可以「一次讀一串」(mbcReadMany),讀到的是同一瞬間的值。
*
* 你需要:神攝手 ESP32CAM(I2C 版韌體)、Arduino UNO/Nano/UNO R4 或 ESP32。
*
* 接線:
* 神攝手 5V → 5V 神攝手 GND → GND
* 神攝手 IO3(SDA)→ A4 神攝手 IO1(SCL)→ A5 (UNO / Nano)
* UNO R4 接 SDA / SCL 腳;ESP32 接 GPIO 21 (SDA) / GPIO 22 (SCL)
* ※ 硬體已確認可接,I2C 版韌體上機待測。IO1/IO3 跟 EV3/SPIKE 串口版共用,韌體二選一。
*
* 準備:
* 先在神攝手背面的螢幕設定好取樣點 S1 和要追的顏色(設定存在機身)。
*
* 怎麼看結果:
* 上傳後,打開「序列埠監控窗」,速度選 115200。拿不同顏色的東西到鏡頭前,看哪些數字在變。
*
* 小提醒:
* 這支是「學校版」韌體用的(編號 01~0A)。WRO 版(HUE12)請看範例 6。
*/
#include <Wire.h> // Arduino 內建的 I2C 通訊工具
const uint8_t ADDR = 0x4C; // 神攝手的「門牌號碼」(I2C 位址),學校版與 WRO 版都是 0x4C
const int16_t MBC_NONE = -32768; // 「沒有這個值/讀不到」的代號
// 小幫手的宣告(實作在檔案最下面);mbcWrite 的參數可以省略
bool mbcWrite(uint8_t addr, uint8_t id, const uint8_t *p = NULL, uint8_t n = 0);
unsigned long lastPrint = 0; // 上一次印的時間
void setup() {
Serial.begin(115200);
mbcBegin();
delay(500);
if (!mbcWaitReady(ADDR, 8000)) {
Serial.println(F("找不到神攝手,請先跑範例 1 檢查接線。"));
while (true) {}
}
Serial.println(F("準備好了!每 0.5 秒印一次所有的值。"));
}
void loop() {
if (millis() - lastPrint >= 500) { // 👉 可以改:每 0.5 秒印一次
lastPrint = millis();
// 編號 01~0A,一次讀 10 個值
int16_t a[10];
if (!mbcReadMany(ADDR, 0x01, 10, a)) { Serial.println(F("讀不到神攝手")); return; }
Serial.println(F("------------------------------"));
Serial.print(F("01 色相 -1~100(-1 = 黑白灰):")); Serial.println(a[0]);
Serial.print(F("02 飽和度 0~100 :")); Serial.println(a[1]);
Serial.print(F("03 明度 0~100 :")); Serial.println(a[2]);
Serial.print(F("04 動態 0~100 :")); Serial.println(a[3]);
Serial.print(F("05 色塊 X 0~100(左 = 0) :")); Serial.println(a[4]);
Serial.print(F("06 色塊 Y 0~75(下 = 0) :")); Serial.println(a[5]);
Serial.print(F("07 色塊寬 0~100 :")); Serial.println(a[6]);
Serial.print(F("08 色塊高 0~75 :")); Serial.println(a[7]);
Serial.print(F("09 色塊面積(寬×高) :")); Serial.println(a[8]);
Serial.print(F("0A 有找到色塊(1 = 有) :")); Serial.println(a[9]);
}
delay(50);
}
// ============================================================
// 以下是「通訊小幫手」(MBC I2C 協議 v2):負責跟感應器講話。
// 所有 MBC 範例都一樣,不用改,也不用看懂。
// 讀法:寫 [編號] → 等 2 ms → 讀 2×k 個 byte = 從該編號起連續 k 個 int16(小端)
// 沒有這個編號(空號)時感應器回 -32768(MBC_NONE)
// ============================================================
// 開始 I2C 通訊(setup 裡呼叫一次)
// UNO/Nano:SDA=A4、SCL=A5;UNO R4:SDA/SCL 腳;ESP32:預設 GPIO21(SDA)/22(SCL)
void mbcBegin() {
Wire.begin();
#if defined(WIRE_HAS_TIMEOUT)
Wire.setWireTimeout(25000, true); // 線沒接好時,不會讓 Arduino 當機卡住
#endif
}
// 從位址 addr 的感應器,由編號 id 起連續讀 k 個值(1~16)放進 out。全部讀到回傳 true
bool mbcReadMany(uint8_t addr, uint8_t id, uint8_t k, int16_t *out) {
if (k < 1) k = 1;
if (k > 16) k = 16; // 一次最多 16 個值(32 byte,Wire 緩衝區上限)
Wire.beginTransmission(addr);
Wire.write(id); // 告訴感應器:我要從第 id 號開始讀
if (Wire.endTransmission() != 0) return false;
delay(2); // 等 2 ms 再讀(ESP32 型的產品一定要等)
uint8_t n = (uint8_t)(2 * k);
if (Wire.requestFrom(addr, n) != n) return false;
for (uint8_t i = 0; i < k; i++) {
uint16_t lo = Wire.read(); // 先來的是低位元組
uint16_t hi = Wire.read(); // 再來高位元組(小端)
out[i] = (int16_t)(uint16_t)(lo | (hi << 8));
}
return true;
}
// 讀一個值。讀不到(沒接好)回傳 MBC_NONE
int16_t mbcRead(uint8_t addr, uint8_t id) {
int16_t v;
return mbcReadMany(addr, id, 1, &v) ? v : MBC_NONE;
}
// 送命令:寫 [id][參數…]。p 指向參數、n = 參數幾個 byte(沒參數就不填)。成功回傳 true
bool mbcWrite(uint8_t addr, uint8_t id, const uint8_t *p, uint8_t n) {
Wire.beginTransmission(addr);
Wire.write(id);
for (uint8_t i = 0; i < n; i++) Wire.write(p[i]);
return Wire.endTransmission() == 0;
}
// 等感應器準備好(狀態旗標 F3 的 bit0 = 1),最多等 ms 毫秒。準備好了回傳 true
bool mbcWaitReady(uint8_t addr, uint16_t ms) {
unsigned long start = millis();
while (millis() - start < ms) {
int16_t f = mbcRead(addr, 0xF3);
if (f != MBC_NONE && (f & 1)) return true;
delay(50);
}
return false;
}你會看到:(取樣點對著綠色紙,畫面右上有一塊目標色)
準備好了!每 0.5 秒印一次所有的值。
------------------------------
01 色相 -1~100(-1 = 黑白灰):34
02 飽和度 0~100 :72
03 明度 0~100 :65
04 動態 0~100 :3
05 色塊 X 0~100(左 = 0) :78
06 色塊 Y 0~75(下 = 0) :60
07 色塊寬 0~100 :22
08 色塊高 0~75 :18
09 色塊面積(寬×高) :396
0A 有找到色塊(1 = 有) :13️⃣ 這是什麼顏色?
做什麼: 讀取樣點 S1 的 01 色相、02 飽和度、03 明度(一次讀 3 個),用中文說顏色名字:色相 −1 時用明度分黑/灰/白。顏色有變才印。
/*
* 神攝手 ESP32CAM × Arduino 範例 3:這是什麼顏色?
* ------------------------------------------------------------
* 這個範例會做什麼?
* 讀取樣點 S1 的顏色(編號 01~03,一次讀 3 個),用中文說出顏色名字:
* 01 色相 0~100:紅 0/100、黃約 17、綠約 33、青約 50、藍約 67、紫約 83;
* -1 = 黑白灰(飽和度太低,沒有顏色)
* 02 飽和度 0~100:越高顏色越鮮豔
* 03 明度 0~100:越高越亮(黑白灰時用它分黑/灰/白)
* 顏色有變才印,畫面不會洗版。
*
* 你需要:神攝手 ESP32CAM(I2C 版韌體)、Arduino UNO/Nano/UNO R4 或 ESP32。
*
* 接線:
* 神攝手 5V → 5V 神攝手 GND → GND
* 神攝手 IO3(SDA)→ A4 神攝手 IO1(SCL)→ A5 (UNO / Nano)
* UNO R4 接 SDA / SCL 腳;ESP32 接 GPIO 21 (SDA) / GPIO 22 (SCL)
* ※ 硬體已確認可接,I2C 版韌體上機待測。IO1/IO3 跟 EV3/SPIKE 串口版共用,韌體二選一。
*
* 準備:
* 先在神攝手背面的螢幕設定好取樣點 S1 和要追的顏色(設定存在機身)。
*
* 怎麼看結果:
* 上傳後,打開「序列埠監控窗」,速度選 115200。拿不同顏色的紙到取樣點前面。
*
* 小提醒:
* 這支是「學校版」韌體用的(編號 01~0A)。WRO 版(HUE12)請看範例 6。
* 分界的數字(例如黃 12~23)可以依你的場地光線調整。
*/
#include <Wire.h> // Arduino 內建的 I2C 通訊工具
const uint8_t ADDR = 0x4C; // 神攝手的「門牌號碼」(I2C 位址),學校版與 WRO 版都是 0x4C
const int16_t MBC_NONE = -32768; // 「沒有這個值/讀不到」的代號
// 小幫手的宣告(實作在檔案最下面);mbcWrite 的參數可以省略
bool mbcWrite(uint8_t addr, uint8_t id, const uint8_t *p = NULL, uint8_t n = 0);
int16_t lastHue = -99, lastSat = -1, lastVal = -1; // 上一次的值(故意用不可能的數字,第一次一定會印)
void setup() {
Serial.begin(115200);
mbcBegin();
delay(500);
if (!mbcWaitReady(ADDR, 8000)) {
Serial.println(F("找不到神攝手,請先跑範例 1 檢查接線。"));
while (true) {}
}
Serial.println(F("準備好了!拿東西到取樣點前面。"));
}
void loop() {
// 一次讀編號 01~03 共 3 個值:[0] 色相 [1] 飽和度 [2] 明度
int16_t c[3];
if (!mbcReadMany(ADDR, 0x01, 3, c)) { delay(100); return; }
// 變化夠大才印(色相差 3 以上、或飽和/明度差 10 以上)
if (abs(c[0] - lastHue) >= 3 || abs(c[1] - lastSat) >= 10 || abs(c[2] - lastVal) >= 10 || (c[0] < 0) != (lastHue < 0)) {
lastHue = c[0]; lastSat = c[1]; lastVal = c[2];
Serial.print(F("顏色:"));
if (c[0] < 0) { // 色相 -1:沒有顏色,用明度分黑/灰/白
if (c[2] < 30) Serial.print(F("黑"));
else if (c[2] < 70) Serial.print(F("灰"));
else Serial.print(F("白"));
} else {
printHueName(c[0]);
}
Serial.print(F(" (色相 ")); Serial.print(c[0]);
Serial.print(F(" 飽和 ")); Serial.print(c[1]);
Serial.print(F(" 明度 ")); Serial.print(c[2]); Serial.println(F(")"));
}
delay(50);
}
// 把色相 0~100 變成顏色名字(紅 0/100、黃約 17、綠約 33、青約 50、藍約 67、紫約 83)
void printHueName(int16_t hue) {
if (hue < 0) Serial.print(F("黑白灰(沒有顏色)"));
else if (hue < 6) Serial.print(F("紅"));
else if (hue < 12) Serial.print(F("橘"));
else if (hue < 23) Serial.print(F("黃"));
else if (hue < 42) Serial.print(F("綠"));
else if (hue < 58) Serial.print(F("青"));
else if (hue < 75) Serial.print(F("藍"));
else if (hue < 92) Serial.print(F("紫"));
else Serial.print(F("紅"));
}
// ============================================================
// 以下是「通訊小幫手」(MBC I2C 協議 v2):負責跟感應器講話。
// 所有 MBC 範例都一樣,不用改,也不用看懂。
// 讀法:寫 [編號] → 等 2 ms → 讀 2×k 個 byte = 從該編號起連續 k 個 int16(小端)
// 沒有這個編號(空號)時感應器回 -32768(MBC_NONE)
// ============================================================
// 開始 I2C 通訊(setup 裡呼叫一次)
// UNO/Nano:SDA=A4、SCL=A5;UNO R4:SDA/SCL 腳;ESP32:預設 GPIO21(SDA)/22(SCL)
void mbcBegin() {
Wire.begin();
#if defined(WIRE_HAS_TIMEOUT)
Wire.setWireTimeout(25000, true); // 線沒接好時,不會讓 Arduino 當機卡住
#endif
}
// 從位址 addr 的感應器,由編號 id 起連續讀 k 個值(1~16)放進 out。全部讀到回傳 true
bool mbcReadMany(uint8_t addr, uint8_t id, uint8_t k, int16_t *out) {
if (k < 1) k = 1;
if (k > 16) k = 16; // 一次最多 16 個值(32 byte,Wire 緩衝區上限)
Wire.beginTransmission(addr);
Wire.write(id); // 告訴感應器:我要從第 id 號開始讀
if (Wire.endTransmission() != 0) return false;
delay(2); // 等 2 ms 再讀(ESP32 型的產品一定要等)
uint8_t n = (uint8_t)(2 * k);
if (Wire.requestFrom(addr, n) != n) return false;
for (uint8_t i = 0; i < k; i++) {
uint16_t lo = Wire.read(); // 先來的是低位元組
uint16_t hi = Wire.read(); // 再來高位元組(小端)
out[i] = (int16_t)(uint16_t)(lo | (hi << 8));
}
return true;
}
// 讀一個值。讀不到(沒接好)回傳 MBC_NONE
int16_t mbcRead(uint8_t addr, uint8_t id) {
int16_t v;
return mbcReadMany(addr, id, 1, &v) ? v : MBC_NONE;
}
// 送命令:寫 [id][參數…]。p 指向參數、n = 參數幾個 byte(沒參數就不填)。成功回傳 true
bool mbcWrite(uint8_t addr, uint8_t id, const uint8_t *p, uint8_t n) {
Wire.beginTransmission(addr);
Wire.write(id);
for (uint8_t i = 0; i < n; i++) Wire.write(p[i]);
return Wire.endTransmission() == 0;
}
// 等感應器準備好(狀態旗標 F3 的 bit0 = 1),最多等 ms 毫秒。準備好了回傳 true
bool mbcWaitReady(uint8_t addr, uint16_t ms) {
unsigned long start = millis();
while (millis() - start < ms) {
int16_t f = mbcRead(addr, 0xF3);
if (f != MBC_NONE && (f & 1)) return true;
delay(50);
}
return false;
}你會看到:(依序拿紅、綠、白紙到取樣點前)
準備好了!拿東西到取樣點前面。
顏色:紅 (色相 2 飽和 80 明度 70)
顏色:綠 (色相 34 飽和 72 明度 65)
顏色:白 (色相 -1 飽和 4 明度 95)提示
分界的數字(例如黃 12~23)在程式最下面的
printHueName,可以依你的場地光線調整。
4️⃣ 色塊在哪裡?
做什麼: 每 0.2 秒讀 05~`0A`(一次讀 6 個):X、Y、寬、高、面積、有找到。用中文說色塊在「左/中/右」「上/中/下」,還有大小。沒找到時 X、Y、寬、高、面積都是 0。
下載 esp32cam_04_blob_position.ino
/*
* 神攝手 ESP32CAM × Arduino 範例 4:色塊在哪裡?
* ------------------------------------------------------------
* 這個範例會做什麼?
* 讀色塊的位置與大小(編號 05~0A,一次讀 6 個):
* 05 X 0~100(左 = 0、右 = 100)、06 Y 0~75(下 = 0、上 = 75)
* 07 寬、08 高、09 面積(寬×高)、0A 有找到(1 = 畫面裡有目標色)
* 每 0.2 秒用中文說色塊在「左/中/右」「上/中/下」,還有大小。
* 沒找到時 X、Y、寬、高、面積都是 0。
*
* 你需要:神攝手 ESP32CAM(I2C 版韌體)、Arduino UNO/Nano/UNO R4 或 ESP32。
*
* 接線:
* 神攝手 5V → 5V 神攝手 GND → GND
* 神攝手 IO3(SDA)→ A4 神攝手 IO1(SCL)→ A5 (UNO / Nano)
* UNO R4 接 SDA / SCL 腳;ESP32 接 GPIO 21 (SDA) / GPIO 22 (SCL)
* ※ 硬體已確認可接,I2C 版韌體上機待測。IO1/IO3 跟 EV3/SPIKE 串口版共用,韌體二選一。
*
* 準備:
* 先在神攝手背面的螢幕設定好取樣點 S1 和要追的顏色(設定存在機身)。
*
* 怎麼看結果:
* 上傳後,打開「序列埠監控窗」,速度選 115200。拿目標顏色的東西在鏡頭前左右、上下、遠近移動。
*
* 小提醒:
* 這支是「學校版」韌體用的(編號 01~0A)。WRO 版(HUE12)請看範例 6。
*/
#include <Wire.h> // Arduino 內建的 I2C 通訊工具
const uint8_t ADDR = 0x4C; // 神攝手的「門牌號碼」(I2C 位址),學校版與 WRO 版都是 0x4C
const int16_t MBC_NONE = -32768; // 「沒有這個值/讀不到」的代號
// 小幫手的宣告(實作在檔案最下面);mbcWrite 的參數可以省略
bool mbcWrite(uint8_t addr, uint8_t id, const uint8_t *p = NULL, uint8_t n = 0);
unsigned long lastPrint = 0;
void setup() {
Serial.begin(115200);
mbcBegin();
delay(500);
if (!mbcWaitReady(ADDR, 8000)) {
Serial.println(F("找不到神攝手,請先跑範例 1 檢查接線。"));
while (true) {}
}
Serial.println(F("準備好了!拿目標顏色的東西到鏡頭前。"));
}
void loop() {
if (millis() - lastPrint >= 200) { // 👉 可以改:每 0.2 秒印一次
lastPrint = millis();
// 一次讀編號 05~0A 共 6 個值:[0] X [1] Y [2] 寬 [3] 高 [4] 面積 [5] 有找到
int16_t b[6];
if (!mbcReadMany(ADDR, 0x05, 6, b)) { Serial.println(F("讀不到神攝手")); return; }
if (!b[5]) { // 0A = 0:畫面裡沒有目標色
Serial.println(F("沒有找到色塊"));
return;
}
// X:0~100 分成左/中/右;Y:0~75 分成下/中/上
Serial.print(F("色塊在 "));
if (b[0] < 35) Serial.print(F("左"));
else if (b[0] > 65) Serial.print(F("右"));
else Serial.print(F("中"));
if (b[1] < 25) Serial.print(F("下"));
else if (b[1] > 50) Serial.print(F("上"));
else Serial.print(F("中"));
Serial.print(F(" X=")); Serial.print(b[0]);
Serial.print(F(" Y=")); Serial.print(b[1]);
Serial.print(F(" 寬 ")); Serial.print(b[2]);
Serial.print(F(" 高 ")); Serial.print(b[3]);
Serial.print(F(" 面積 ")); Serial.print(b[4]);
if (b[4] > 2000) Serial.print(F("(很近/很大)"));
else if (b[4] < 100) Serial.print(F("(很遠/很小)"));
Serial.println();
}
delay(50);
}
// ============================================================
// 以下是「通訊小幫手」(MBC I2C 協議 v2):負責跟感應器講話。
// 所有 MBC 範例都一樣,不用改,也不用看懂。
// 讀法:寫 [編號] → 等 2 ms → 讀 2×k 個 byte = 從該編號起連續 k 個 int16(小端)
// 沒有這個編號(空號)時感應器回 -32768(MBC_NONE)
// ============================================================
// 開始 I2C 通訊(setup 裡呼叫一次)
// UNO/Nano:SDA=A4、SCL=A5;UNO R4:SDA/SCL 腳;ESP32:預設 GPIO21(SDA)/22(SCL)
void mbcBegin() {
Wire.begin();
#if defined(WIRE_HAS_TIMEOUT)
Wire.setWireTimeout(25000, true); // 線沒接好時,不會讓 Arduino 當機卡住
#endif
}
// 從位址 addr 的感應器,由編號 id 起連續讀 k 個值(1~16)放進 out。全部讀到回傳 true
bool mbcReadMany(uint8_t addr, uint8_t id, uint8_t k, int16_t *out) {
if (k < 1) k = 1;
if (k > 16) k = 16; // 一次最多 16 個值(32 byte,Wire 緩衝區上限)
Wire.beginTransmission(addr);
Wire.write(id); // 告訴感應器:我要從第 id 號開始讀
if (Wire.endTransmission() != 0) return false;
delay(2); // 等 2 ms 再讀(ESP32 型的產品一定要等)
uint8_t n = (uint8_t)(2 * k);
if (Wire.requestFrom(addr, n) != n) return false;
for (uint8_t i = 0; i < k; i++) {
uint16_t lo = Wire.read(); // 先來的是低位元組
uint16_t hi = Wire.read(); // 再來高位元組(小端)
out[i] = (int16_t)(uint16_t)(lo | (hi << 8));
}
return true;
}
// 讀一個值。讀不到(沒接好)回傳 MBC_NONE
int16_t mbcRead(uint8_t addr, uint8_t id) {
int16_t v;
return mbcReadMany(addr, id, 1, &v) ? v : MBC_NONE;
}
// 送命令:寫 [id][參數…]。p 指向參數、n = 參數幾個 byte(沒參數就不填)。成功回傳 true
bool mbcWrite(uint8_t addr, uint8_t id, const uint8_t *p, uint8_t n) {
Wire.beginTransmission(addr);
Wire.write(id);
for (uint8_t i = 0; i < n; i++) Wire.write(p[i]);
return Wire.endTransmission() == 0;
}
// 等感應器準備好(狀態旗標 F3 的 bit0 = 1),最多等 ms 毫秒。準備好了回傳 true
bool mbcWaitReady(uint8_t addr, uint16_t ms) {
unsigned long start = millis();
while (millis() - start < ms) {
int16_t f = mbcRead(addr, 0xF3);
if (f != MBC_NONE && (f & 1)) return true;
delay(50);
}
return false;
}你會看到:(目標物從右上移到正中間、靠近,然後拿走)
準備好了!拿目標顏色的東西到鏡頭前。
色塊在 右上 X=78 Y=60 寬 22 高 18 面積 396
色塊在 中中 X=50 Y=38 寬 60 高 45 面積 2700(很近/很大)
沒有找到色塊注意
Y 是「下 = 0、上 = 75」,跟一般螢幕座標相反。
5️⃣ 伺服馬達跟著色塊轉(進階)
做什麼: 把神攝手固定在 SG90 伺服馬達上,色塊往哪邊跑,馬達就往哪邊轉:05 色塊 X 0100 → 20160 度;沒找到就回 90 度。不用安裝任何函式庫:UNO/Nano/R4 自己每 20 ms 送一個脈衝,ESP32 用內建的 ledc 送 50 Hz 訊號(程式用 #if defined(ESP32) 分開)。
另外要準備: SG90 伺服馬達 1 顆。
伺服馬達接線:
| SG90 的線 | Arduino UNO / Nano / R4 | ESP32 |
|---|---|---|
| 棕色 | GND | GND |
| 紅色 | 5V | 5V(VIN) |
| 橘色(訊號) | 9 | GPIO 13 |
下載 esp32cam_05_track_servo.ino
/*
* 神攝手 ESP32CAM × Arduino 範例 5:伺服馬達跟著色塊轉(進階)
* ------------------------------------------------------------
* 這個範例會做什麼?
* 把神攝手裝在 SG90 伺服馬達上,色塊往哪邊跑,馬達就往哪邊轉:
* 讀編號 05 色塊 X(0~100)→ 換算成角度,送給伺服馬達。
* 沒找到色塊(0A = 0)就回到 90 度中間。
* 不用安裝任何函式庫:UNO/Nano/UNO R4 自己每 20 ms 送一個脈衝;ESP32 用內建 ledc 送 50 Hz 訊號。
*
* 你需要:神攝手 ESP32CAM(I2C 版韌體)、Arduino UNO/Nano/UNO R4 或 ESP32。另外要:SG90 伺服馬達 1 顆。
*
* 接線:
* 神攝手 5V → 5V 神攝手 GND → GND
* 神攝手 IO3(SDA)→ A4 神攝手 IO1(SCL)→ A5 (UNO / Nano)
* UNO R4 接 SDA / SCL 腳;ESP32 接 GPIO 21 (SDA) / GPIO 22 (SCL)
* ※ 硬體已確認可接,I2C 版韌體上機待測。IO1/IO3 跟 EV3/SPIKE 串口版共用,韌體二選一。
* SG90 棕線 → GND 紅線 → 5V 橘線(訊號)→ 9
* (ESP32 的訊號線請接 GPIO 13)
*
* 準備:
* 先在神攝手背面的螢幕設定好取樣點 S1 和要追的顏色(設定存在機身)。
* 把神攝手固定在伺服馬達的擺臂上,鏡頭朝前。
*
* 怎麼看結果:
* 拿目標顏色的東西左右移動,伺服馬達會跟著轉。序列埠監控窗(115200)會印角度。
*
* 小提醒:
* 這支是「學校版」韌體用的(編號 01~0A)。WRO 版(HUE12)請看範例 6。
* 轉的方向跟色塊相反的話,把 REVERSE 改成 true。
* 伺服馬達一直抖或 Arduino 一直重開:USB 電不夠,紅線、棕線改接外接電池(4~6V),負極也要接 GND。
*/
#include <Wire.h>
const uint8_t ADDR = 0x4C; // 神攝手的「門牌號碼」(I2C 位址),學校版與 WRO 版都是 0x4C
const int16_t MBC_NONE = -32768; // 「沒有這個值/讀不到」的代號
// 小幫手的宣告(實作在檔案最下面);mbcWrite 的參數可以省略
bool mbcWrite(uint8_t addr, uint8_t id, const uint8_t *p = NULL, uint8_t n = 0);
#if defined(ESP32)
const int SERVO_PIN = 13; // ESP32:訊號線接 GPIO 13
#else
const int SERVO_PIN = 9; // UNO / Nano / R4:訊號線接第 9 腳
#endif
// ---- 👉 可以改的參數 ----
const bool REVERSE = false; // 轉的方向反了就改 true
const int MIN_ANGLE = 20; // 色塊在最左時的角度
const int MAX_ANGLE = 160; // 色塊在最右時的角度
int angle = 90;
unsigned long lastRead = 0;
unsigned long lastPrint = 0;
void setup() {
Serial.begin(115200);
servoBegin(); // 準備伺服馬達(UNO 自己送脈衝、ESP32 用 ledc)
servoWrite(90); // 先轉到中間
mbcBegin();
delay(500);
if (!mbcWaitReady(ADDR, 8000)) {
Serial.println(F("找不到神攝手,請先跑範例 1 檢查接線。"));
while (true) {}
}
Serial.println(F("準備好了!拿目標顏色的東西左右移動。"));
}
void loop() {
// ---- 1. 每 0.1 秒讀一次神攝手:編號 05~0A 共 6 個值:[0] X … [5] 有找到 ----
if (millis() - lastRead >= 100) {
lastRead = millis();
int16_t b[6];
if (!mbcReadMany(ADDR, 0x05, 6, b) || !b[5]) {
angle = 90; // 讀不到、或沒找到色塊 → 回中間
} else {
int x = constrain(b[0], 0, 100);
angle = REVERSE ? map(x, 0, 100, MAX_ANGLE, MIN_ANGLE) : map(x, 0, 100, MIN_ANGLE, MAX_ANGLE);
}
}
// ---- 2. 每一圈都送訊號給伺服馬達(UNO 要每 20 ms 送一次脈衝,它才會停在那個角度) ----
servoWrite(angle);
if (millis() - lastPrint >= 300) { // 每 0.3 秒印一次角度
lastPrint = millis();
Serial.print(F("角度:")); Serial.print(angle); Serial.println(F(" 度"));
}
delay(20); // 這支不是 50 ms:伺服馬達的脈衝要每 20 ms 一次
}
// ---- 伺服馬達:兩種板子兩種做法,呼叫方式一樣 ----
// 0 度 ≈ 0.544 ms、90 度 ≈ 1.47 ms、180 度 ≈ 2.4 ms 高電位,每 20 ms 一次
#if defined(ESP32)
// ESP32 用內建的 ledc 送 50 Hz(20 ms 一次)PWM,設定一次就會自己一直送
void servoBegin() {
ledcAttach(SERVO_PIN, 50, 16); // 50 Hz、16 位解析度(0~65535)
}
void servoWrite(int deg) {
deg = constrain(deg, 0, 180);
long us = map(deg, 0, 180, 544, 2400); // 角度 → 微秒
ledcWrite(SERVO_PIN, (uint32_t)(us * 65535L / 20000L)); // 20000 微秒 = 一個週期
}
#else
// UNO / Nano / R4 不用函式庫:自己把腳拉高「幾微秒」再拉低,就是一個脈衝
void servoBegin() { pinMode(SERVO_PIN, OUTPUT); }
void servoWrite(int deg) {
deg = constrain(deg, 0, 180);
int us = map(deg, 0, 180, 544, 2400); // 角度 → 微秒
digitalWrite(SERVO_PIN, HIGH);
delayMicroseconds(us);
digitalWrite(SERVO_PIN, LOW);
}
#endif
// ============================================================
// 以下是「通訊小幫手」(MBC I2C 協議 v2):負責跟感應器講話。
// 所有 MBC 範例都一樣,不用改,也不用看懂。
// 讀法:寫 [編號] → 等 2 ms → 讀 2×k 個 byte = 從該編號起連續 k 個 int16(小端)
// 沒有這個編號(空號)時感應器回 -32768(MBC_NONE)
// ============================================================
// 開始 I2C 通訊(setup 裡呼叫一次)
// UNO/Nano:SDA=A4、SCL=A5;UNO R4:SDA/SCL 腳;ESP32:預設 GPIO21(SDA)/22(SCL)
void mbcBegin() {
Wire.begin();
#if defined(WIRE_HAS_TIMEOUT)
Wire.setWireTimeout(25000, true); // 線沒接好時,不會讓 Arduino 當機卡住
#endif
}
// 從位址 addr 的感應器,由編號 id 起連續讀 k 個值(1~16)放進 out。全部讀到回傳 true
bool mbcReadMany(uint8_t addr, uint8_t id, uint8_t k, int16_t *out) {
if (k < 1) k = 1;
if (k > 16) k = 16; // 一次最多 16 個值(32 byte,Wire 緩衝區上限)
Wire.beginTransmission(addr);
Wire.write(id); // 告訴感應器:我要從第 id 號開始讀
if (Wire.endTransmission() != 0) return false;
delay(2); // 等 2 ms 再讀(ESP32 型的產品一定要等)
uint8_t n = (uint8_t)(2 * k);
if (Wire.requestFrom(addr, n) != n) return false;
for (uint8_t i = 0; i < k; i++) {
uint16_t lo = Wire.read(); // 先來的是低位元組
uint16_t hi = Wire.read(); // 再來高位元組(小端)
out[i] = (int16_t)(uint16_t)(lo | (hi << 8));
}
return true;
}
// 讀一個值。讀不到(沒接好)回傳 MBC_NONE
int16_t mbcRead(uint8_t addr, uint8_t id) {
int16_t v;
return mbcReadMany(addr, id, 1, &v) ? v : MBC_NONE;
}
// 送命令:寫 [id][參數…]。p 指向參數、n = 參數幾個 byte(沒參數就不填)。成功回傳 true
bool mbcWrite(uint8_t addr, uint8_t id, const uint8_t *p, uint8_t n) {
Wire.beginTransmission(addr);
Wire.write(id);
for (uint8_t i = 0; i < n; i++) Wire.write(p[i]);
return Wire.endTransmission() == 0;
}
// 等感應器準備好(狀態旗標 F3 的 bit0 = 1),最多等 ms 毫秒。準備好了回傳 true
bool mbcWaitReady(uint8_t addr, uint16_t ms) {
unsigned long start = millis();
while (millis() - start < ms) {
int16_t f = mbcRead(addr, 0xF3);
if (f != MBC_NONE && (f & 1)) return true;
delay(50);
}
return false;
}你會看到:(目標物從左邊移到右邊,然後拿走)
準備好了!拿目標顏色的東西左右移動。
角度:34 度
角度:90 度
角度:146 度
角度:90 度提示
- 轉的方向跟色塊相反:把
REVERSE改成true。- 伺服馬達一直抖、或 Arduino 一直重開機:USB 的電不夠,紅線、棕線改接外接電池(4~6 V),負極也要接 Arduino GND。
- 這支範例 loop 末是
delay(20)不是 50:UNO 的伺服脈衝要每 20 ms 送一次。
6️⃣ WRO 版:12 個色點一次看
做什麼: 給燒 WRO 版(HUE12)韌體的神攝手用:每 0.5 秒把 12 個色點 S1S12 印成 12 格。101B 顏色碼一次讀 12 個、20~`2B` 鮮豔度一次讀 12 個,每格印顏色名字+顏色碼+鮮豔度。
下載 esp32cam_06_wro_12_spots.ino
/*
* 神攝手 ESP32CAM × Arduino 範例 6:WRO 版:12 個色點一次看
* ------------------------------------------------------------
* 這個範例會做什麼?
* 這支是給「WRO 版(HUE12)」韌體用的:畫面上 12 個色點 S1~S12,每 0.5 秒印成 12 格。
* 10~1B:S1~S12 顏色碼(一次讀 12 個)
* 0~239 彩色(色相環縮成 0~239)、240~245 白、246~250 黑、255 = 這個色點沒開
* 20~2B:S1~S12 鮮豔度 0~255(一次讀 12 個;黑 = 1、沒開 = 0)
* 每一格印出顏色名字+顏色碼+鮮豔度。
*
* 你需要:神攝手 ESP32CAM(WRO 版 HUE12 的 I2C 版韌體)、Arduino UNO/Nano/UNO R4 或 ESP32。
*
* 接線:
* 神攝手 5V → 5V 神攝手 GND → GND
* 神攝手 IO3(SDA)→ A4 神攝手 IO1(SCL)→ A5 (UNO / Nano)
* UNO R4 接 SDA / SCL 腳;ESP32 接 GPIO 21 (SDA) / GPIO 22 (SCL)
* ※ 硬體已確認可接,I2C 版韌體上機待測。IO1/IO3 跟 EV3/SPIKE 串口版共用,韌體二選一。
*
* 準備:
* 先在神攝手背面的螢幕開啟要用的色點 S1~S12,並對準場地。
*
* 怎麼看結果:
* 上傳後,打開「序列埠監控窗」,速度選 115200。
*
* 小提醒:
* 學校版韌體讀 10~2B 會全部是 -32768(空號),請改跑範例 2~5。
* 顏色碼 255 只代表「色點沒開」;色點有開但沒對到東西時,仍會回該點目前的顏色。
*/
#include <Wire.h> // Arduino 內建的 I2C 通訊工具
const uint8_t ADDR = 0x4C; // 神攝手的「門牌號碼」(I2C 位址),學校版與 WRO 版都是 0x4C
const int16_t MBC_NONE = -32768; // 「沒有這個值/讀不到」的代號
// 小幫手的宣告(實作在檔案最下面);mbcWrite 的參數可以省略
bool mbcWrite(uint8_t addr, uint8_t id, const uint8_t *p = NULL, uint8_t n = 0);
unsigned long lastPrint = 0;
void setup() {
Serial.begin(115200);
mbcBegin();
delay(500);
if (!mbcWaitReady(ADDR, 8000)) {
Serial.println(F("找不到神攝手,請先跑範例 1 檢查接線。"));
while (true) {}
}
// 確認是 WRO 版(F0 高 byte = 2);學校版也能跑,只是會讀到空號
int16_t id = mbcRead(ADDR, 0xF0);
if (((id >> 8) & 0xFF) != 2) Serial.println(F("注意:這台不是 WRO 版韌體,10~2B 會是空號(-32768)。"));
Serial.println(F("準備好了!每 0.5 秒印一次 12 個色點。"));
}
void loop() {
if (millis() - lastPrint >= 500) { // 👉 可以改:每 0.5 秒印一次
lastPrint = millis();
int16_t code[12], vivid[12];
if (!mbcReadMany(ADDR, 0x10, 12, code)) { Serial.println(F("讀不到(顏色碼)")); return; } // 10~1B
if (!mbcReadMany(ADDR, 0x20, 12, vivid)) { Serial.println(F("讀不到(鮮豔度)")); return; } // 20~2B
Serial.println(F("---- S1 ~ S12 ----"));
for (uint8_t i = 0; i < 12; i++) {
Serial.print(F("S")); Serial.print(i + 1); Serial.print(i < 9 ? F(" ") : F(" "));
printCodeName(code[i]);
Serial.print(F(" 碼 ")); Serial.print(code[i]);
Serial.print(F(" 鮮豔 ")); Serial.println(vivid[i]);
}
}
delay(50);
}
// 顏色碼 → 名字:0~239 彩色(把 0~239 當成色相環 0~360 度)、240~245 白、246~250 黑、255 沒開
void printCodeName(int16_t c) {
if (c == MBC_NONE) { Serial.print(F("(空號)")); return; }
if (c == 255) { Serial.print(F("沒開 ")); return; }
if (c >= 246) { Serial.print(F("黑 ")); return; }
if (c >= 240) { Serial.print(F("白 ")); return; }
int deg = (int)((long)c * 360 / 240); // 0~239 → 0~359 度
if (deg < 15) Serial.print(F("紅 "));
else if (deg < 45) Serial.print(F("橘 "));
else if (deg < 75) Serial.print(F("黃 "));
else if (deg < 165) Serial.print(F("綠 "));
else if (deg < 200) Serial.print(F("青 "));
else if (deg < 265) Serial.print(F("藍 "));
else if (deg < 330) Serial.print(F("紫 "));
else Serial.print(F("紅 "));
}
// ============================================================
// 以下是「通訊小幫手」(MBC I2C 協議 v2):負責跟感應器講話。
// 所有 MBC 範例都一樣,不用改,也不用看懂。
// 讀法:寫 [編號] → 等 2 ms → 讀 2×k 個 byte = 從該編號起連續 k 個 int16(小端)
// 沒有這個編號(空號)時感應器回 -32768(MBC_NONE)
// ============================================================
// 開始 I2C 通訊(setup 裡呼叫一次)
// UNO/Nano:SDA=A4、SCL=A5;UNO R4:SDA/SCL 腳;ESP32:預設 GPIO21(SDA)/22(SCL)
void mbcBegin() {
Wire.begin();
#if defined(WIRE_HAS_TIMEOUT)
Wire.setWireTimeout(25000, true); // 線沒接好時,不會讓 Arduino 當機卡住
#endif
}
// 從位址 addr 的感應器,由編號 id 起連續讀 k 個值(1~16)放進 out。全部讀到回傳 true
bool mbcReadMany(uint8_t addr, uint8_t id, uint8_t k, int16_t *out) {
if (k < 1) k = 1;
if (k > 16) k = 16; // 一次最多 16 個值(32 byte,Wire 緩衝區上限)
Wire.beginTransmission(addr);
Wire.write(id); // 告訴感應器:我要從第 id 號開始讀
if (Wire.endTransmission() != 0) return false;
delay(2); // 等 2 ms 再讀(ESP32 型的產品一定要等)
uint8_t n = (uint8_t)(2 * k);
if (Wire.requestFrom(addr, n) != n) return false;
for (uint8_t i = 0; i < k; i++) {
uint16_t lo = Wire.read(); // 先來的是低位元組
uint16_t hi = Wire.read(); // 再來高位元組(小端)
out[i] = (int16_t)(uint16_t)(lo | (hi << 8));
}
return true;
}
// 讀一個值。讀不到(沒接好)回傳 MBC_NONE
int16_t mbcRead(uint8_t addr, uint8_t id) {
int16_t v;
return mbcReadMany(addr, id, 1, &v) ? v : MBC_NONE;
}
// 送命令:寫 [id][參數…]。p 指向參數、n = 參數幾個 byte(沒參數就不填)。成功回傳 true
bool mbcWrite(uint8_t addr, uint8_t id, const uint8_t *p, uint8_t n) {
Wire.beginTransmission(addr);
Wire.write(id);
for (uint8_t i = 0; i < n; i++) Wire.write(p[i]);
return Wire.endTransmission() == 0;
}
// 等感應器準備好(狀態旗標 F3 的 bit0 = 1),最多等 ms 毫秒。準備好了回傳 true
bool mbcWaitReady(uint8_t addr, uint16_t ms) {
unsigned long start = millis();
while (millis() - start < ms) {
int16_t f = mbcRead(addr, 0xF3);
if (f != MBC_NONE && (f & 1)) return true;
delay(50);
}
return false;
}你會看到:(開了 S1~S3,其他沒開)
準備好了!每 0.5 秒印一次 12 個色點。
---- S1 ~ S12 ----
S1 紅 碼 3 鮮豔 210
S2 綠 碼 80 鮮豔 180
S3 白 碼 243 鮮豔 12
S4 沒開 碼 255 鮮豔 0
S5 沒開 碼 255 鮮豔 0
S6 沒開 碼 255 鮮豔 0
S7 沒開 碼 255 鮮豔 0
S8 沒開 碼 255 鮮豔 0
S9 沒開 碼 255 鮮豔 0
S10 沒開 碼 255 鮮豔 0
S11 沒開 碼 255 鮮豔 0
S12 沒開 碼 255 鮮豔 0注意
- 顏色碼
255只代表「色點沒開」;色點有開但沒對到東西時,仍會回那一點目前的顏色。- 學校版韌體跑這支會讀到
-32768(空號),程式開頭會提醒。
常見問題
Q:一直出現「找不到神攝手」?
- 檢查 IO3(SDA)、IO1(SCL)有沒有接反(UNO:IO3 → A4、IO1 → A5;R4:SDA/SCL;ESP32:21/22)。
- 檢查 5V、GND 有沒有插緊。神攝手的螢幕有沒有亮?
- 神攝手燒的是 I2C 版韌體嗎?EV3 版、SPIKE 版走串口,接 Arduino 讀不到。
- 神攝手開機要幾秒,範例會等 8 秒;還是不行就按 Arduino 的 RESET 鍵重來。
Q:序列埠監控窗一堆亂碼? 速度沒選 115200,在監控窗右下角改好就正常了。
Q:色相一直是 0? 取樣點 S1 還沒設定(在神攝手背面螢幕設定,存在機身);或者取樣點對著紅色(紅 = 0 也 = 100)。
Q:色塊 X、Y 都是 0?
畫面裡沒有目標顏色(0A = 0)。先在機身螢幕設定要追的顏色與 HSV 容許範圍,看螢幕上有沒有框住色塊。
Q:讀到 -32768 是什麼意思?
這是「沒有這個值」的代號(範例裡叫 MBC_NONE):學校版讀 102B、WRO 版讀 010A 都會是這個;讀不到神攝手(線鬆了)也是。寫程式時先檢查 v != MBC_NONE 再用。
Q:小幫手裡的 delay(2) 可以拿掉嗎?
不行。神攝手是 ESP32 做的從機,它不能把 I2C 時脈拉住等資料,主機寫完編號後要等 2 毫秒它才準備好。拿掉會讀到上一次的資料。
Q:ESP32 當主機時要注意什麼?
一樣要 delay(2);接線用 GPIO 21(SDA)/22(SCL)。範例 5 的伺服馬達在 ESP32 用 ledcAttach/ledcWrite(ESP32 核心 3.x),訊號線接 GPIO 13。
Q:想知道每一個格子放什麼資料? 請看上面的 指令總覽 和 MBC 通用 I2C 協議 v2(給會寫程式的人看的進階資料)。
其他問題
- 一直讀到 -32768? 代表模組沒回應:檢查 SDA/SCL 有沒有接反、位址對不對、5V/GND 有沒有接好。
- 可以接很多顆嗎? 可以,每顆位址要不同;同型號用命令 F8 換位址(見通用 I2C 協議)。
還有問題?到 聯絡我們 附上你用的軟體和程式截圖。
下載
本頁依據
- 主機與軟體清單:
_build/guides.json - 值與命令:
_build/protocol/cam_school.json、_build/protocol/cam_wro.json(單一真相來源) - 安裝與設定:
_build/guides/software/arduino-ide.zh.md、_build/guides/product/esp32cam/arduino-ide.zh.md - 範例:
sensors/esp32cam/arduino-examples(-wro).md