در این پست قصد دارم برد ESP32CAM رو تبدیل به دوربین عکاسی کنم اون رو روی حافظه microSD ذخیره کنم. اگر اولین بار هست که قصد دارید با برد ESP32CAM کار کنید پیشنهاد می کنم اول این آموزش و این آموزش رو مطالعه کنید بعد برگردید اینجا و ادامه بدید. در این پست از مدار خاصی استفاده نمی کنیم. فقط مثل قبل برد FTDI رو به برد ESP32CAM متصل می کنیم فقط باید ذخیره تصاویر باید یک حافظه microSD رو طبق روش آماده کنید و درون اسلات مربوطه قرار بدید.
رویه کار به این صورت هست بعد آپلود کد و ریست شدن برد به حالت خواب سنگین یا Deep Sleep میره. در این حالت هست که برد حداقل جریان مصرفی رو داره. به محض اینکه دکمه ریست روی برد رو فشار میدید برد از حالت Deep Sleep خارج میشه. تصویر رو تسخیر میکنه، تصویر تسخیر شده رو روی حافظه ذخیره میکنه و مجددا وارد حالت Deep Sleep میشه.
قبل از هر اقدامی باید برای ذخیره تصاویر کارت SD رو آماده کنیم. در دیتاشیت برد قید شده حداکثر حافظه پشتیبانی ۴ گیگابایت هست اما من از یک حافظه ۱۶ گیگابایتی استفاده کردم و به مشکلی برخورد نکردم. در ضمن این رو بگم حجم تصاویر پایین هست و مشکلی از بابت حجم مورد استفاده توسط برد وجود نداره. مطابق تصاویر زیر بر روی درایو SD در ویندوز کلیک راست میکنید و کارت رو بصورت FAT32 فرمت می کنید. دقت کنید اگر کارت بصورت NTFS فرمت بشه ذخیره سازی تصاویر انجام نمیشه.
خب حالا یک پروژه یا اصطلاحا یک اسکچ جدید در Ardiuno IDE ایجاد کنید کد زیر در اون کپی کنید
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
#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() { } |
در ابتداکتابخونه های مورد نیاز برای کار با دوربین رو به برنامه اضافه می کنیم. همچنین چون می خوایم از حافظه EEPROM استفاده کنیمT کتابخونه مربوطه رو استفاده می کنیم.
1 2 3 4 5 6 7 8 |
#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 |
خب قبل از اینکه قسمت بعدی رو توضیح بدم اجازه بدید اول یک موضوع رو روشن کنم براتون. ما زمانی که عکس میگیگیریم (خخخ) و قصد داریم اون رو ذخیره کنیم باید براش یک اسم انتخاب کنیم. و از اونجائیکه نام فایل باید به بطور خودکار تولید بشه باید شماره فایلی که قبلا ذخیره شده رو در جایی سیوکنیم و در هر بار عکس گرفتن یک شماره به اون عدد ذخیره شده اضافه میکنیم و مجدد اون رو سیو میکنیم. محل ذخیره شدن فایل هم حافظه EEPROM خود برد ESP32CAM هست. به این منظور برای استفاده از این حافظه همین اول برنامه باید مشخص کنیم از چند خونه حافظه استفاده می کنیم. چون من نمی خوام بیشتر ۲۰۰ تا عکس برداری انجام بدم ( کلی پول برد رو دادم دکمه ریستش خراب میشه خو!!!) میزان حافظه EEPROM رو ۱ بایت در نظر میگیرم. در این حالت مقدارشمارنده اسم فایل از ۰ تا نهایتا ۲۵۵ تغییر میکنه و جمعا میشه ۲۵۶ عکس. اگر میخواید بیشتر از این تعداد عکس ذخیره کنید ۲ بایت استفاده کنید. که همونطور می بینید من یک بایت استفاده کردم:
1 |
#define EEPROM_SIZE 1 |
در ادامه باید شماره پین ها متصل به دوربین برد رو تعریف کنیم. من چون از برد AI-THINKER استفاده می کنم تعاریف رو بر اساس اون انجام میدم. اگر برد شما متفاوت هست در آموزش راه اندازی ویدئو سرور با قابلیت تشخیص چهره تعارف سایر دوربین ها وجود داره که میتونید از اون بخش کد در اینجا استفاده کنید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// 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 |
برای اینکه مقدار شمارنده نام فایل رو از EEPROM بخونیم باید اول در یک متغیر قرارش بدیم. تعریف متغیر رو اینجا انجام میدیم:
1 |
int pictureNumber = 0; |
خب حالا نوبت به تنظیمات خود دوربین هست که در اینجا تنظیمات رو ست می کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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; |
خب بعضی از دوربین ها دارای حافظه داینامیک PSRAM هستن که بهشون اجازه میده تصویر رو با کیفیت بالاتری ذخیره کنن. بعضی سازنده های برد ESP32CAM از دوربین PSRAM دار استفاده میکنن و بعضی از PSRAM ندار. برد من که AI-THINKER هست استفاده میکنه (اگه ندارید دلتون بسوزه) از بقیه بردها خبر ندارم. برای اینکه مطمئن بشیم کد برنامه تصویر رو با بالاترین کیفیت ممکن ذخیره میکنه وجود PSRAM در دوربین رو بررسی میکنیم و براساس اون دوربین رو کانفیگ می کنیم.
1 2 3 4 5 6 7 8 9 10 |
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; } |
اینجا تنظیمات رو به تابع esp_camera_init پاس میدیم . اگر خروجی این تابع برابر با ثابت ESP_OK بود که هیچ در غیر اینصورت خطایی به وجود اومده که اون رو چاپ می کنیم. کجا؟ خب معلومه دیگه خروجی عزیز سریال:
1 2 3 4 5 6 |
// 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; } |
حالا نوبت راه اندازی کارت microSD هست:
1 2 3 4 5 6 7 8 9 10 11 |
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; } |
این قسمت از کد هم عکس رو میگیره که اگه نتونه بگیره عین بچه ننه ها میره به خروجی سریال میگه:
1 2 3 4 5 6 7 8 |
camera_fb_t * fb = NULL; // Take Picture with Camera fb = esp_camera_fb_get(); if(!fb) { Serial.println("Camera capture failed"); return; } |
یادتونه جلوتر کلی در مورد حافظه EEPROM و شماره فایل و آی بیشتر از ۲۰۰ تا عکس نشه بردم خراب میشه و بقیه چرت و پرت ها صحبت کردم؟ آها اینجا ازش استفاده میکنم :
1 |
EEPROM.begin(EEPROM_SIZE); |
شمارنده عکس رو که از EEPROM خوندیم یکی بهش اضافه می کنیم :
1 |
pictureNumber = EEPROM.read(0) + 1; |
برای ذخیره تصاویر در microSD شمارنده عکس میچسبونیم ته کلمه picture و در مسیر اصلی حافظه ذخیرش میکنیم اینطوری:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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(); |
بعد از اینکه تصویر رو ذخیره کردیم شمارنده ای رو که یکی بهش اضافه کرده بودبم رو دوباره در حافظه EEPROM ذخیره می کنیم:
1 2 |
EEPROM.write(0, pictureNumber); EEPROM.commit(); |
خب این برد ۱۰ دلاری کمی خنگ تشریف دارن ینی چی؟ ینی وقتی شما عکس رو که گرفتی و LED مربوطه فلش زد تو همون حالت هپروت روشن میمونه برای همین برای با کد زیر غیر فعالش می کنی:
1 2 3 |
pinMode(4, OUTPUT); digitalWrite(4, LOW); rtc_gpio_hold_en(GPIO_NUM_4); |
بعد که همه کارهامون رو انجام دادیم بهش میگیم کپه رو بذار برو به حالت Deep Sleep … اینطوری:
1 |
esp_deep_sleep_start(); |
و تمام در مورد نحوه اتصال FTDI پروگرامر و نحوه پروگرام کردن این برد به اندازه کافی تو دو تا آمورشی که در اول پست معرفی کردم صحبت کردم پس دیگه این آموزش رو شلوغش نمی کنم.
در ابتدا کار میتونید خروجی سریال رو برای بررسی صحت عملکرد برد بررسی کنید. بعد از هر بار عکس برداری اطلاعات زیر چاپ میشه :
بعد از اینکه از صحت کار برد مطمئن شدید خیلی راحت FTDIپروگرامر رو از برد جدا کنید و پایه های ۵V و GND برد رو به یک منبع تغذیه مطمئن مثل پاور بانک متصل کنید. تبریک میگم شما صاحب یک دوربین دست ساز بی ریخت مثل تصویر زیر شدید: