這個麥克納姆輪小車底盤長25.5cm 寬15cm。底盤採用鋁合金材質,堅固耐用。
麥克納姆輪採用注塑工藝製造,並非 3D 列印,質量可靠。麥輪直徑約 60mm,適用於市售制式小車底盤。
準備材料
這個麥克納姆輪小車底盤長25.5cm 寬15cm。底盤採用鋁合金材質,堅固耐用。
麥克納姆輪採用注塑工藝製造,並非 3D 列印,質量可靠。麥輪直徑約 60mm,適用於市售制式小車底盤。
ESP32-Cam 雖然有攝影鏡頭,但若是固定式的,它的視野將只侷限在一個限定的區域。透過這組「ESP32-Cam單軸雲台」套件,可以讓ESP32-Cam的鏡頭左右各擺動90度,讓您的視野更加廣闊。
本套件可以很方便安裝在各式市售的小車底盤上。
準備材料
或是可以到下列賣場購買
奇摩 https://tw.bid.yahoo.com/item/100984760432
露天 https://www.ruten.com.tw/item/show?22049213363904
組裝步驟
Step1 用 M2 螺絲和螺帽將伺服馬達固定到支架。建議讓轉軸置中。
Step3 用 M2 螺絲和螺帽將 L 型塑膠片鎖到大壓克力板上。並用束帶將 ESP32-Cam 開發板固定到大壓克力板上。
Step4 組合大壓克力板部件和搖臂部件,用 M2 螺絲和螺帽將 L 型塑膠片鎖緊到搖臂部件上。
Step5 用 M3 螺絲和螺帽將伺服馬達部件鎖到小車底盤上。
Step6 將 Step4 部件放置到伺服馬達上,並用馬達所附之 M2.5 螺絲固定。
Step7 試寫程式上傳到 ESP32-Cam,並測試鏡頭左右擺動90度是否正常。至此完成。
特雷門(Theremin) 電波琴是甚麼玩意兒? 如果您還不知道,可以參考一下維基百科 。
所需材料
1. Arduino Nano(Uno也可以) 開發板 *1
2. 麵包板 *1
3. PAM8403 功放模組 *1
4. 光敏電阻(LDR) *1
5. 10K 電阻 *1
6. 小喇叭(蜂鳴器也可以) *1
7. 公母頭杜邦線 *若干
8. 公排針 *若干
如果您沒有以上材料,也可以到露天賣場購買。
1. 將 Arduino Nano 開發板插上麵包板。
2. 依照上面電路圖用杜邦線連接各單元。
3. 喇叭接功放模組單邊輸出即可。
程式
1. 下載函式庫 https://sensorium.github.io/Mozzi/
2. 將下列程式上傳到 Arduino。
//#include <ADC.h>
#include <MozziGuts.h>
#include <Oscil.h> // oscillator template
#include <tables/sin2048_int8.h> // sine table for oscillator
#include <RollingAverage.h>
#include <ControlDelay.h>
#define INPUT_PIN 0 // analog control input
unsigned int echo_cells_1 = 32;
unsigned int echo_cells_2 = 60;
unsigned int echo_cells_3 = 127;
#define CONTROL_RATE 64
ControlDelay <128, int> kDelay; // 2seconds
// oscils to compare bumpy to averaged control input
Oscil <SIN2048_NUM_CELLS, AUDIO_RATE> aSin0(SIN2048_DATA);
Oscil <SIN2048_NUM_CELLS, AUDIO_RATE> aSin1(SIN2048_DATA);
Oscil <SIN2048_NUM_CELLS, AUDIO_RATE> aSin2(SIN2048_DATA);
Oscil <SIN2048_NUM_CELLS, AUDIO_RATE> aSin3(SIN2048_DATA);
// use: RollingAverage <number_type, how_many_to_average> myThing
RollingAverage <int, 32> kAverage; // how_many_to_average has to be power of 2
int averaged;
void setup(){
kDelay.set(echo_cells_1);
startMozzi();
}
void updateControl(){
int bumpy_input = mozziAnalogRead(INPUT_PIN);
averaged = kAverage.next(bumpy_input);
aSin0.setFreq(averaged);
aSin1.setFreq(kDelay.next(averaged));
aSin2.setFreq(kDelay.read(echo_cells_2));
aSin3.setFreq(kDelay.read(echo_cells_3));
}
int updateAudio(){
return 3*((int)aSin0.next()+aSin1.next()+(aSin2.next()>>1)
+(aSin3.next()>>2)) >>3;
}
void loop(){
audioHook();
}
上傳程式後,試著用手由遠到近遮住光敏電阻,光敏電阻受光程度會影響聲音的頻率。
觀賞影片
參考資料
https://www.hackster.io/Oniichan_is_ded/arduino-theremin-96fc6d?fbclid=IwAR0IxHx7tyu2cwGUJTBs6tmBP234j-XV-2stRpmPg76N2sv9NQQpdhUsFKk
採購資訊
特雷門(Theremin) 電波琴套件 https://www.ruten.com.tw/item/show?22040528261934
本文是參考「硬派筆記 用 ESPAsyncWebServer 建立你的第一個網頁」,在實做過程中的一些補充。
本文適用於 ESP 系列開發板,包括 ESP8266/ESP32/ESP32-Cam。
將硬派筆記內的程式碼上傳到板子時,可能會出現找不到 AsyncTCP.h 和 ESPAsyncWebServer.h 等函式庫,可以到此下載
由於是把 ESP32 開發板當作 AP,因此在程式上傳完畢後,需要中斷您的網路連線,並重新連線到 ESP32 所命名的網路「ESP_WiFi」。
鍵入密碼「12345678」。
然後在您的電腦瀏覽器內鍵入網址「192.168.4.1」。
隨後再鍵入「192.168.4.1/test」。
演練成功。
本文旨在說明如何用手機透過藍牙跟 ESP32-Cam 作溝通,看完之後您將會發現這將是一件非常簡單的事。
ESP32-Cam 開發板搭載了一個低功耗藍牙,它跟一般的 HC-05、HC-06 藍牙有點兒不同,這方面您可以參考一下維基百科,在「無線電介面」章節裡有詳細介紹他們的區別。
ESP32-Cam 發送訊息給手機
以下這個程式會讓 ESP32-Cam 每隔 1 秒鐘發送一次 "Hello World!" 字串給手機。
#include <BluetoothSerial.h>
BluetoothSerial BT; // 宣告藍芽物件,名稱為BT
void setup() {
Serial.begin(115200);
BT.begin("RobotWolfGroup"); // 指定藍芽裝置的名稱
}
void loop() {
BT.println("Hello World!");
delay(1000);
}
您有沒有發現程式碼才短短幾行?
是的,免設定密碼、鮑率...等有的沒的,它就是這麼簡單好用。
藍牙配對
在 ESP32-Cam 端程式上傳完畢後,接下來需要用手機跟它的藍牙配對。
Step1 打開手機藍牙,選擇「配對新裝置」。
Step2 找到「RobotWolfGroup」這個藍牙裝置後,點擊它並配對。這個 BLE 在配對時是不需要輸入密碼的。
手機安裝 APP
我們需要在手機裡安裝 APP,才可以接收來自 BLE 的訊息。
Step1 請打開手機的「PLAY商店」,在裡面搜尋「Arduino Bluetooth」。
Step2 找到「Arduino Bluetooth Control」,安裝它。
Step3 安裝完成後,請開啟它。開啟後如下圖
Step4 點選畫面右上角落的圓形雙箭頭,搜尋「RobotWolfGroup」藍牙裝置,找到後點選它。
Step5 再點選畫面右上角落的「Terminal」,然後您就會看到如下畫面
手機發送訊息給 ESP32-Cam
以下這個程式可以讓手機發送訊息給 ESP32-Cam,請將該程式上傳到 ESP32-Cam。
#include <BluetoothSerial.h>
BluetoothSerial BT;//宣告藍芽物件,名稱為BT
void setup() {
Serial.begin(115200);
BT.begin("RobotWolfGroup"); //指定藍芽裝置的名稱
}
void loop() {
//檢查藍芽內是否有資料
while (BT.available()) {
//讀取藍芽資料
String str = BT.readString();
//顯示在序列視窗
Serial.println(str);
}
delay(10);
}
上傳完成後,請打開「序列埠監看視窗」。
開啟手機 APP 並完成選取藍牙裝置(如果之前有順利選取可用裝置,當再次啟動 APP 時它會自動連線」。
點選畫面右上角落的「Terminal」,然後在視窗最下方的「Type in data to send」欄位內輸入訊息,然後您就會看到如下畫面。
接著請您再檢查一下「序列埠監視視窗」裡是否有接收到訊息,如下圖
手機收發 ESP32-Cam 訊息
將上面兩個程式合併,就可以讓手機收發 ESP32-Cam 訊息。
這個程式是手機鍵入 "A",ESP32-Cam 會回應 "Apple";手機鍵入 "B",ESP32-Cam 會回應 "Banana";若手機鍵入 "A" 、"B" 以外的字,ESP32-Cam 會回應 "N/A"。
請將下方程式上傳到 ESP32-Cam。
#include <BluetoothSerial.h>
BluetoothSerial BT; // 宣告藍芽物件,名稱為BT
void setup() {
Serial.begin(115200);
BT.begin("RobotWolfGroup"); // 指定藍芽裝置的名稱
}
void loop() {
// 檢查藍芽內是否有資料
while (BT.available()) {
// 讀取藍芽資料
String str = BT.readString();
// 顯示在序列視窗
Serial.println(str);
// 回訊給手機
if (str == "A")
BT.println("Apple");
else if (str == "B")
BT.println("Banana");
else
BT.println("N/A");
}
delay(10);
}
開啟手機 APP 並完成選取藍牙裝置(如果之前有順利選取可用裝置,當再次啟動 APP 時它會自動連線」。
點選畫面右上角落的「Terminal」,然後在視窗最下方的「Type in data to send」欄位內輸入訊息,然後您就會看到如下畫面。
使用幾次之後,發現這訊息時間不對,而且訊息一去一回大約花了 2 秒鐘時間,速度還真是慢啊,應該是這個 APP 的問題吧?
不過這個 APP 還是有可取之處,它的語音控制功能 (Voice Control) 還不錯,可以設定 10 組的語音與相對應要發送的字串,這個用來控制家電倒是蠻不錯的,我用它來控制 5 種電器的開與關。
語音控制功能這部分跟 ESP32-Cam 無關,我們就不多介紹了,那就請您自行練習囉。
採購資訊
Micro SD 4G 記憶卡 https://www.ruten.com.tw/item/show?22033106394834
ESP32 開發指南(書) https://www.ruten.com.tw/item/show?22023657661505
ESP32-Cam 開發板 https://www.ruten.com.tw/item/show?22018501441929
ESP32-Cam 雙軸雲台(基本款)套件 https://www.ruten.com.tw/item/show?22024688065979
ESP32-Cam 雙軸雲台(人體偵測款)套件 https://www.ruten.com.tw/item/show?22024688076465
ESP32-Cam 板載 Micro SD 記憶卡既然擁有可以存取檔案的功能,那麼如何對資料夾和檔案的建立/列示/修改/刪除/更名...等操作,就是我們必學的課題。
底下程式把這些課題都演練了一遍,您可以先把程式上傳到板子,然後打開「序列埠監視視窗」,仔細觀察整個程式運作過程。以下為您說明視窗內的訊息,請注意反白部分。
首先是列示 SD 卡的性質
列出根目錄下的所有資料夾和檔案
新建一個 <mydir> 資料夾 > 列示根目錄狀況 > 移除資料夾 > 再次列示根目錄狀況
新建一個叫 hello.txt 的檔案 > 將字串附加到檔案 > 讀取並印出檔案內容
我想有必要為上面的過程再詳加說明一下...
首先,它新建一個叫 hello.txt 的檔案 ,同時寫入 "Hello " 字串,
然後將字串 "World!\n" 附加到檔案裡,這個 "\n" 就是換行,
最後讀取並印出檔案內容 "Hello Wolrd!"。
訊息窗內都是一些簡單的英文,請您自行閱讀,我們的介紹就到這裡了。
程式碼
#include "FS.h"
#include "SD_MMC.h"
//List dir in SD card
void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
Serial.printf("Listing directory: %s\n", dirname);
File root = fs.open(dirname);
if(!root){
Serial.println("Failed to open directory");
return;
}
if(!root.isDirectory()){
Serial.println("Not a directory");
return;
}
File file = root.openNextFile();
while(file){
if(file.isDirectory()){
Serial.print(" DIR : ");
Serial.println(file.name());
if(levels){
listDir(fs, file.name(), levels -1);
}
} else {
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print(" SIZE: ");
Serial.println(file.size());
}
file = root.openNextFile();
}
}
//Create a dir in SD card
void createDir(fs::FS &fs, const char * path){
Serial.printf("Creating Dir: %s\n", path);
if(fs.mkdir(path)){
Serial.println("Dir created");
} else {
Serial.println("mkdir failed");
}
}
//delete a dir in SD card
void removeDir(fs::FS &fs, const char * path){
Serial.printf("Removing Dir: %s\n", path);
if(fs.rmdir(path)){
Serial.println("Dir removed");
} else {
Serial.println("rmdir failed");
}
}
//Read a file in SD card
void readFile(fs::FS &fs, const char * path){
Serial.printf("Reading file: %s\n", path);
File file = fs.open(path);
if(!file){
Serial.println("Failed to open file for reading");
return;
}
Serial.print("Read from file: ");
while(file.available()){
Serial.write(file.read());
}
}
//Write a file in SD card
void writeFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Writing file: %s\n", path);
File file = fs.open(path, FILE_WRITE);
if(!file){
Serial.println("Failed to open file for writing");
return;
}
//fwrite(fb->buf, 1, fb->len, file);
if(file.print(message)){
Serial.println("File written");
} else {
Serial.println("Write failed");
}
}
//Append to the end of file in SD card
void appendFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Appending to file: %s\n", path);
File file = fs.open(path, FILE_APPEND);
if(!file){
Serial.println("Failed to open file for appending");
return;
}
if(file.print(message)){
Serial.println("Message appended");
} else {
Serial.println("Append failed");
}
}
//Rename a file in SD card
void renameFile(fs::FS &fs, const char * path1, const char * path2){
Serial.printf("Renaming file %s to %s\n", path1, path2);
if (fs.rename(path1, path2)) {
Serial.println("File renamed");
} else {
Serial.println("Rename failed");
}
}
//Delete a file in SD card
void deleteFile(fs::FS &fs, const char * path){
Serial.printf("Deleting file: %s\n", path);
if(fs.remove(path)){
Serial.println("File deleted");
} else {
Serial.println("Delete failed");
}
}
//Test read and write speed using test.txt file
void testFileIO(fs::FS &fs, const char * path){
File file = fs.open(path);
static uint8_t buf[512];
size_t len = 0;
uint32_t start = millis();
uint32_t end = start;
if(file){
len = file.size();
size_t flen = len;
start = millis();
while(len){
size_t toRead = len;
if(toRead > 512){
toRead = 512;
}
file.read(buf, toRead);
len -= toRead;
}
end = millis() - start;
Serial.printf("%u bytes read for %u ms\n", flen, end);
file.close();
} else {
Serial.println("Failed to open file for reading");
}
file = fs.open(path, FILE_WRITE);
if(!file){
Serial.println("Failed to open file for writing");
return;
}
size_t i;
start = millis();
for(i=0; i<2048; i++){
file.write(buf, 512);
}
end = millis() - start;
Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end);
file.close();
}
void setup() {
Serial.begin(115200);
Serial.println("SDcard Testing....");
if(!SD_MMC.begin()){
Serial.println("Card Mount Failed");
return;
}
uint8_t cardType = SD_MMC.cardType();
if(cardType == CARD_NONE){
Serial.println("No SD_MMC card attached");
return;
}
Serial.print("SD_MMC Card Type: ");
if(cardType == CARD_MMC){
Serial.println("MMC");
} else if(cardType == CARD_SD){
Serial.println("SDSC");
} else if(cardType == CARD_SDHC){
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024);
Serial.printf("SD_MMC Card Size: %lluMB\n", cardSize);
listDir(SD_MMC, "/", 0);
createDir(SD_MMC, "/mydir");
listDir(SD_MMC, "/", 0);
removeDir(SD_MMC, "/mydir");
listDir(SD_MMC, "/", 2);
writeFile(SD_MMC, "/hello.txt", "Hello ");
appendFile(SD_MMC, "/hello.txt", "World!\n");
readFile(SD_MMC, "/hello.txt");
deleteFile(SD_MMC, "/foo.txt");
renameFile(SD_MMC, "/hello.txt", "/foo.txt");
readFile(SD_MMC, "/foo.txt");
testFileIO(SD_MMC, "/test.txt");
Serial.printf("Total space: %lluMB\n", SD_MMC.totalBytes() / (1024 * 1024));
Serial.printf("Used space: %lluMB\n", SD_MMC.usedBytes() / (1024 * 1024));
}
void loop() {
// put your main code here, to run repeatedly:
delay(10000);
}
採購資訊
Micro SD 4G 記憶卡 https://www.ruten.com.tw/item/show?22033106394834
ESP32 開發指南(書) https://www.ruten.com.tw/item/show?22023657661505
ESP32-Cam 開發板 https://www.ruten.com.tw/item/show?22018501441929
ESP32-Cam 雙軸雲台(基本款)套件 https://www.ruten.com.tw/item/show?22024688065979
ESP32-Cam 雙軸雲台(人體偵測款)套件 https://www.ruten.com.tw/item/show?22024688076465
ESP32-Cam 板載 Micro SD 記憶卡插槽,可以用來儲存相片等資料,如果您要做大量的資料傳輸,也可以把它拿來當作緩衝區使用。
原廠文件顯示 ESP32-Cam 最大只能用到 4G 記憶卡,但有網友表示 16G 也可以。
格式化記憶卡
記憶卡在使用前必須先格式化,格式化的規格有 NTFS、FAT、FAT32。
有一些記憶卡在出廠前即已完成格式化,在您將它插入電腦後可以確認一下。如果尚未格式化或是您想重新格式化,可以依照下列步驟實施。
Step1 將記憶卡插入電腦,打開「我的電腦」或「檔案管理員」。
Step2 用滑鼠右鍵點擊「抽取式磁碟」,再選取「格式化...」,出現視窗
Step3 選項「檔案系統」選取「FAT32」,按一下「開始」按鈕。格式化完成後會顯示如下視窗
格式化好記憶卡後,接下來進入主題--拍照並儲存到記憶卡。
這個程式的流程如下
進入睡眠模式 > 按下[重置按鈕] 喚醒 > 拍照 > 儲存到記憶卡 > (回到最前頭的進入睡眠模式)
認識板載 Micro SD 記憶卡插槽
ESP32-Cam 使用數個 GPIO 腳位控制 Micro SD 記憶卡,如下圖左邊的腳位全用上了
根據上面的腳位圖和電路圖,我們整理出 ESP32-Cam 和 Micro SD 腳位關係表,如下
上述連接 Micro SD 的腳位,如果沒有在使用存取記憶卡功能時,可以設定輸出和輸入等 IO 功能。
同時,這些 GPIO 腳位都是 RTC,也都支援 ADC 功能。
程式碼
#include "esp_camera.h"
#include "Arduino.h"
#include "FS.h" // SD Card ESP32
#include "SD_MMC.h" // SD Card ESP32
#include "soc/soc.h" // Disable brownour problems
#include "soc/rtc_cntl_reg.h" // Disable brownour problems
#include "driver/rtc_io.h"
#include <EEPROM.h> // read and write from flash memory
// define the number of bytes you want to access
#define EEPROM_SIZE 1
// Pin definition for CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
int pictureNumber = 0;
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
Serial.begin(115200);
//Serial.setDebugOutput(true);
//Serial.println();
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
if(psramFound()){
config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
// Init Camera
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
//Serial.println("Starting SD Card");
if(!SD_MMC.begin()){
Serial.println("SD Card Mount Failed");
return;
}
uint8_t cardType = SD_MMC.cardType();
if(cardType == CARD_NONE){
Serial.println("No SD Card attached");
return;
}
camera_fb_t * fb = NULL;
// Take Picture with Camera
fb = esp_camera_fb_get();
if(!fb) {
Serial.println("Camera capture failed");
return;
}
// initialize EEPROM with predefined size
EEPROM.begin(EEPROM_SIZE);
pictureNumber = EEPROM.read(0) + 1;
// Path where new picture will be saved in SD Card
String path = "/picture" + String(pictureNumber) +".jpg";
fs::FS &fs = SD_MMC;
Serial.printf("Picture file name: %s\n", path.c_str());
File file = fs.open(path.c_str(), FILE_WRITE);
if(!file){
Serial.println("Failed to open file in writing mode");
}
else {
file.write(fb->buf, fb->len); // payload (image), payload length
Serial.printf("Saved file to path: %s\n", path.c_str());
EEPROM.write(0, pictureNumber);
EEPROM.commit();
}
file.close();
esp_camera_fb_return(fb);
// Turns off the ESP32-CAM white on-board LED (flash) connected to GPIO 4
pinMode(4, OUTPUT);
digitalWrite(4, LOW);
rtc_gpio_hold_en(GPIO_NUM_4);
delay(2000);
Serial.println("Going to sleep now");
delay(2000);
esp_deep_sleep_start();
Serial.println("This will never be printed");
}
void loop() {
}
將上面的程式上傳到板子,
移除 GPIO0 和 Gnd 的接線,
打開「序列埠監視視窗」,
按一下板子上的 [Reset] 鍵,
您會發現閃光燈亮了一下,同時「序列埠監視視窗」顯示它已將一張照片儲存到記憶卡內。
然後呢?
它又進入睡眠模式,
除非您又按一下 [Reset] 鍵,
如此周而復始。
最後,我們用電腦檢查剛剛 ESP32-Cam 在記憶卡裡面儲存了哪些東西,如下
檔名是 picture???,後面數字是流水號。
檔案類型是 jpg
大小是 315KB
我查看了一下它的內容,如下
如果要作為長時間監控拍攝或縮時攝影,有必要把照片的尺寸和解析度縮小才行。
採購資訊
Micro SD 4G 記憶卡 https://www.ruten.com.tw/item/show?22033106394834
ESP32 開發指南(書) https://www.ruten.com.tw/item/show?22023657661505
ESP32-Cam 開發板 https://www.ruten.com.tw/item/show?22018501441929
ESP32-Cam 雙軸雲台(基本款)套件 https://www.ruten.com.tw/item/show?22024688065979
ESP32-Cam 雙軸雲台(人體偵測款)套件 https://www.ruten.com.tw/item/show?22024688076465