TTGO T-Camera Plus ESP32 คือบอร์ด ESP32 ที่มีกล้อง OV2640 ความละเอียด 2 ล้านพิเซล ในตัว พร้อมจอภาพ IPS Panel ST7789 ขนาด 1.3 นิ้ว ซึ่งแอปพลิเคชั่นกล้องต้องการหน่วยความจำเพิ่มเติมดังนั้นจึงมีเพิ่ม Micro SD Card เข้าที่ช่องเสียบการ์ด
### อุปกรณ์ที่ใช้ ###
1. TTGO T-Camera Plus ESP32-DOWDQ6 Display Camera Module
2. Micro USB Cable Wire 1m
3. Micro SD Card Class 10 16GB + Card Adapter
โดยการทำโปรเจคมีขั้นตอนดังนี้
1.ติดตั้ง Arduino core for ESP32
ลิงค์การติดตั้ง Arduino core for ESP32
https://robotsiam.blogspot.com/2017/09/arduino-core-for-esp32.html
2.ติดตั้ง ไลบรารี่ arduino-selfie-camera
2.1 ดาวน์โหลด ไลบรารี่ arduino-selfie-camera
https://github.com/moononournation/arduino-selfie-camera
2.2 คลิกที่ Clone or download -> Download ZIP
2.3 เปิด โปรแกรม Arduino IDE ไปที่ Skecth -> Include Library -> Add .ZIP Library...
2.4 ไปที่ ไลบรารี arduino-selfie-camera-master.zip ที่เรา ดาวน์โหลด มา -> Open
2.5 ตรวจสอบที่ Skecth -> Include Library จะพบ ไลบรารี arduino-selfie-camera-master เพิ่มเข้ามาใน Arduino IDE ของเรา
3.ฟอร์แมต Micro SD Card
ติดตั้งโปรแกรม SD Card Formatter 5.0 ใช้สำหรับ Format Disk สามารถดาวน์โหลดได้จากลิงก์
https://www.sdcard.org/downloads/formatter_4/eula_windows/
จากนั้นให้คลิกปุ่ม Accept โปรแกรม SD Formatter โปรแกรมจึงจะดาวน์โหลดสู่ คอมพิวเตอร์ของเรา
ให้เสียบ Micro SD Card เข้าไปใน Card Adapter
Card Adapter ที่มี Micro SD Card อยู่ด้านใน
แล้วจึงเสียบ Card Adapter ที่บรรจุ Micro SD Card ใส่ในช่องซ็อกเก็ตของคอมพิวเตอร์ PC (ดันจนกระทั่งมีเสียงคลิก)
แล้วทำการ Format , Micro SD Card โดยใช้โปรแกรม SD Card Formatter
ถ้าเสียบ Card Adapter ที่บรรจุ Micro SD Card ได้ถูกต้อง ที่ Select card จะมี ไดรฟ์ ให้เลือก ในตัวอย่างจะเป็น ไดรฟ์ F:/
เลือก Quick format และที่ Volume label ในตัวอย่างตั้งชื่อเป็น Robot
คลิก Format
คลิก Yes
คลิก OK ถึงขั้นตอนนี้การ Format SD Card ของเรานั้นเสร็จสมบูรณ์แล้ว ให้ปิดโปรแกรมลงไป
4.เสียบ Micro SD Card เข้าไปใน Slot Card
.
5.อัพโหลดโปรแกรม
5.1 เชื่อมต่อสาย USB ระหว่าง คอมพิวเตอร์ กับ ESP32
5.2 เปิดโปรแกรม Arduino (IDE) และ ก็อปปี้ โค้ดด้านล่างนี้ ไปวางไว้ในส่วนเขียนโปรแกรม
#include <esp_camera.h>
#include <SD.h>
#include <FS.h>
#include <rom/tjpgd.h>
#include "cam.h"
#include "ST7789.h"
#include "tjpgdec.h"
#define SDCARA_CS 0
#define SNAP_QUALITY 6 // 1-63, 1 is the best
ST7789 tft = ST7789(); // Invoke library, pins defined in User_Setup.h
char tmpStr[256];
char nextFilename[31];
uint16_t fileIdx = 0;
int i = 0;
static uint16_t *preview;
sensor_t *s;
camera_fb_t *fb = NULL;
JPGIODEV dev;
char *work = NULL; // Pointer to the working buffer (must be 4-byte aligned)
void setup()
{
Serial.begin(115200);
// Serial.setDebugOutput(true);
tft.init();
tft.invertDisplay(true);
tft.setSwapBytes(true);
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
tft.setTextDatum(TL_DATUM);
tft.setTextSize(2);
tft.setTextColor(TFT_WHITE, TFT_RED);
tft.drawString(" ESP", 0, 0);
tft.setTextColor(TFT_WHITE, TFT_ORANGE);
tft.drawString("32 ", 48, 0);
tft.setTextColor(TFT_WHITE, TFT_GREEN);
tft.drawString(" Camera ", 82, 0);
tft.setTextColor(TFT_WHITE, TFT_BLUE);
tft.drawString(" Plus ", 172, 0);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextSize(1);
if (!SD.begin(SDCARA_CS))
{
tft.drawString("SD Init Fail!", 0, 208);
Serial.println("SD Init Fail!");
}
else
{
snprintf(tmpStr, sizeof(tmpStr), "SD Card Type: %d Size: %lu MB", SD.cardType(), SD.cardSize() / 1024 / 1024);
tft.drawString(tmpStr, 0, 208);
Serial.println(tmpStr);
init_folder();
xTaskCreate(
findNextFileIdxTask, /* Task function. */
"FindNextFileIdxTask", /* String with name of task. */
10000, /* Stack size in bytes. */
work, /* Parameter passed as input of the task */
1, /* Priority of the task. */
NULL); /* Task handle. */
}
esp_err_t err = cam_init();
if (err != ESP_OK)
{
snprintf(tmpStr, sizeof(tmpStr), "Camera init failed with error 0x%x", err);
tft.drawString(tmpStr, 0, 208);
Serial.println(tmpStr);
}
//drop down frame size for higher initial frame rate
s = esp_camera_sensor_get();
s->set_brightness(s, 2);
s->set_contrast(s, 2);
s->set_saturation(s, 2);
s->set_sharpness(s, 2);
s->set_aec2(s, true);
s->set_denoise(s, true);
s->set_lenc(s, true);
s->set_hmirror(s, true);
//s->set_vflip(s, true);
s->set_quality(s, 63);
preview = new uint16_t[200 * 150];
work = (char *)malloc(WORK_BUF_SIZE);
dev.linbuf_idx = 0;
dev.x = 0;
dev.y = 0;
dev.linbuf[0] = (color_t *)heap_caps_malloc(JPG_IMAGE_LINE_BUF_SIZE * 3, MALLOC_CAP_DMA);
dev.linbuf[1] = (color_t *)heap_caps_malloc(JPG_IMAGE_LINE_BUF_SIZE * 3, MALLOC_CAP_DMA);
}
esp_err_t cam_init()
{
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;
// init with high specs to pre-allocate larger buffers
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality = SNAP_QUALITY;
config.fb_count = 2;
// camera init
return esp_camera_init(&config);
}
void init_folder()
{
File file = SD.open("/DCIM");
if (!file)
{
Serial.println("Create /DCIM");
SD.mkdir("/DCIM");
}
else
{
Serial.println("Found /DCIM");
file.close();
}
file = SD.open("/DCIM/100ESPDC");
if (!file)
{
Serial.println("Create /DCIM/100ESPDC");
SD.mkdir("/DCIM/100ESPDC");
}
else
{
Serial.println("Found /DCIM/100ESPDC");
file.close();
}
}
void findNextFileIdxTask(void *parameter)
{
findNextFileIdx();
vTaskDelete(NULL);
}
void findNextFileIdx()
{ // TODO: revise ugly code
fileIdx++;
File file;
snprintf(nextFilename, sizeof(nextFilename), "/DCIM/100ESPDC/DSC%05D.JPG", fileIdx);
file = SD.open(nextFilename);
if (!file)
{
Serial.printf("Next file: %s\n", nextFilename);
return;
}
else
{
for (int k = 1000; k <= 30000; k += 1000)
{
snprintf(nextFilename, sizeof(nextFilename), "/DCIM/100ESPDC/DSC%05D.JPG", fileIdx + k);
file = SD.open(nextFilename);
if (file)
{
Serial.printf("Found %s\n", nextFilename);
file.close();
}
else
{
Serial.printf("Not found %s\n", nextFilename);
k -= 1000;
for (int h = 100; h <= 1000; h += 100)
{
snprintf(nextFilename, sizeof(nextFilename), "/DCIM/100ESPDC/DSC%05D.JPG", fileIdx + k + h);
file = SD.open(nextFilename);
if (file)
{
Serial.printf("Found %s\n", nextFilename);
file.close();
}
else
{
Serial.printf("Not found %s\n", nextFilename);
h -= 100;
for (int t = 10; t <= 100; t += 10)
{
snprintf(nextFilename, sizeof(nextFilename), "/DCIM/100ESPDC/DSC%05D.JPG", fileIdx + k + h + t);
file = SD.open(nextFilename);
if (file)
{
Serial.printf("Found %s\n", nextFilename);
file.close();
}
else
{
Serial.printf("Not found %s\n", nextFilename);
t -= 10;
for (int d = 1; d <= 10; d++)
{
snprintf(nextFilename, sizeof(nextFilename), "/DCIM/100ESPDC/DSC%05D.JPG", fileIdx + k + h + t + d);
file = SD.open(nextFilename);
if (file)
{
Serial.printf("Found %s\n", nextFilename);
file.close();
}
else
{
Serial.printf("Next file: %s\n", nextFilename);
fileIdx += k + h + t + d;
return;
}
}
}
}
}
}
}
}
}
}
void snap()
{
s->set_hmirror(s, false);
//s->set_vflip(s, false);
s->set_quality(s, SNAP_QUALITY);
tft.fillRect(20, 45, 200, 150, TFT_DARKGREY);
fb = esp_camera_fb_get();
esp_camera_fb_return(fb);
fb = NULL;
tft.fillRect(20, 45, 200, 150, TFT_LIGHTGREY);
fb = esp_camera_fb_get();
if (!fb)
{
tft.drawString("Camera capture JPG failed", 0, 208);
Serial.println("Camera capture JPG failed");
}
else
{
File file = SD.open(nextFilename, FILE_WRITE);
if (file.write(fb->buf, fb->len))
{
tft.fillRect(20, 45, 200, 150, TFT_LIGHTGREY);
snprintf(tmpStr, sizeof(tmpStr), "File written: %luKB\n%s", fb->len / 1024, nextFilename);
tft.drawString(tmpStr, 0, 224);
Serial.println(tmpStr);
}
else
{
tft.drawString("Write failed!", 0, 224);
Serial.println("Write failed!");
}
esp_camera_fb_return(fb);
fb = NULL;
file.close();
}
s->set_hmirror(s, true);
//s->set_vflip(s, true);
s->set_quality(s, 63);
fb = esp_camera_fb_get();
esp_camera_fb_return(fb);
fb = NULL;
}
void enterSleep()
{
tft.end();
esp_deep_sleep_start();
}
void loop()
{
if (i == 1) // count down
{
tft.setTextSize(2);
tft.drawString("3", 116, 24);
Serial.println("3");
}
else if (i == 5)
{
tft.setTextSize(2);
tft.drawString("2", 116, 24);
Serial.println("2");
}
else if (i == 9)
{
tft.setTextSize(2);
tft.drawString("1", 116, 24);
Serial.println("1");
}
else if (i == 13) // start snap
{
tft.setTextSize(2);
tft.drawString("Cheeze!", 92, 24);
Serial.println("Cheeze!");
snap();
}
else if (i == 15)
{
tft.setTextSize(2);
tft.drawString("Cheeze!!", 92, 24);
Serial.println("Cheeze!!");
findNextFileIdx();
snap();
}
else if (i == 17)
{
tft.setTextSize(2);
tft.drawString("Cheeze!!!", 92, 24);
Serial.println("Cheeze!!!");
findNextFileIdx();
snap();
tft.setTextSize(2);
tft.drawString("Reset to snap again!", 0, 24);
Serial.println("Reset to snap again!");
decodeJpegFile(nextFilename, 3);
tft.pushRect(20, 45, 200, 150, preview);
delay(5000);
Serial.println("Enter deep sleep...");
enterSleep();
}
else
{
fb = esp_camera_fb_get();
if (!fb)
{
Serial.printf("Camera capture failed!");
tft.drawString("Camera capture failed!", 0, 224);
}
else
{
decodeJpegBuff(fb->buf, fb->len, 3);
tft.pushRect(20, 45, 200, 150, preview);
esp_camera_fb_return(fb);
fb = NULL;
}
}
i++;
}
// User defined call-back function to input JPEG data from file
//---------------------
static UINT tjd_file_input (
JDEC* jd, // Decompression object
BYTE* buff, // Pointer to the read buffer (NULL:skip)
UINT nd // Number of bytes to read/skip from input stream
)
{
int rb = 0;
// Device identifier for the session (5th argument of jd_prepare function)
JPGIODEV *dev = (JPGIODEV*)jd->device;
if (buff) { // Read nd bytes from the input strem
rb = dev->f.read(buff, nd);
return rb; // Returns actual number of bytes read
}
else { // Remove nd bytes from the input stream
if (dev->f.seek(dev->f.position() + nd)) return nd;
else return 0;
}
}
// User defined call-back function to input JPEG data from memory buffer
//-------------------------
static UINT tjd_buf_input(
JDEC *jd, // Decompression object
BYTE *buff, // Pointer to the read buffer (NULL:skip)
UINT nd // Number of bytes to read/skip from input stream
)
{
// Device identifier for the session (5th argument of jd_prepare function)
JPGIODEV *dev = (JPGIODEV *)jd->device;
if (!dev->membuff)
return 0;
if (dev->bufptr >= (dev->bufsize + 2))
return 0; // end of stream
if ((dev->bufptr + nd) > (dev->bufsize + 2))
nd = (dev->bufsize + 2) - dev->bufptr;
if (buff)
{ // Read nd bytes from the input strem
memcpy(buff, dev->membuff + dev->bufptr, nd);
dev->bufptr += nd;
return nd; // Returns number of bytes read
}
else
{ // Remove nd bytes from the input stream
dev->bufptr += nd;
return nd;
}
}
// User defined call-back function to output RGB bitmap to display device
//----------------------
static UINT tjd_output(
JDEC *jd, // Decompression object of current session
void *bitmap, // Bitmap data to be output
JRECT *rect // Rectangular region to output
)
{
BYTE *src = (BYTE *)bitmap;
// Serial.printf("%d, %d, %d, %d\n", rect->top, rect->left, rect->bottom, rect->right);
for (int y = rect->top; y <= rect->bottom; y++)
{
for (int x = rect->left; x <= rect->right; x++)
{
preview[y * 200 + x] = tft.color565(*(src++), *(src++), *(src++));
}
}
return 1; // Continue to decompression
}
void decodeJpegBuff(uint8_t arrayname[], uint32_t array_size, uint8_t scale)
{
JDEC jd; // Decompression object (70 bytes)
JRESULT rc;
//dev.fhndl = NULL;
// image from buffer
dev.membuff = arrayname;
dev.bufsize = array_size;
dev.bufptr = 0;
if (scale > 3)
scale = 3;
if (work)
{
rc = jd_prepare(&jd, tjd_buf_input, (void *)work, WORK_BUF_SIZE, &dev);
if (rc == JDR_OK)
{
// Start to decode the JPEG file
rc = jd_decomp(&jd, tjd_output, scale);
}
}
}
void decodeJpegFile(char filename[], uint8_t scale)
{
JDEC jd; // Decompression object (70 bytes)
JRESULT rc;
dev.f = SD.open(filename);
// image from buffer
//dev.membuff = null;
dev.bufsize = JPG_IMAGE_LINE_BUF_SIZE;
dev.bufptr = 0;
if (scale > 3)
scale = 3;
if (work)
{
rc = jd_prepare(&jd, tjd_file_input, (void *)work, WORK_BUF_SIZE, &dev);
if (rc == JDR_OK)
{
// Start to decode the JPEG file
rc = jd_decomp(&jd, tjd_output, scale);
}
}
}
ดาวน์โหลดโค้ด :
https://drive.google.com/open?id=1qsyOCNZRPyIpr72bs7f1DzmVua4qYKX9
5.3 ไปที่ Tools -> Board เลือก ESP32 Dev Module
5.4 ไปที่ Tools -> PSRAM: เลือกเป็น Enabled
5.5 ไปที่ Tools -> Port แล้วเลือกพอร์ตที่ปรากฏ (กรณีใช้เครื่องคอมพิวเตอร์ที่มี COM Port ให้เลือกตัวอื่นที่ไม่ใช่ COM1)
ในตัวอย่างเลือกเป็น "COM20"
5.6 กดปุ่ม เพื่ออัพโหลด
ตั้งชื่อไฟล์ -> Save โปรแกรม จะทำการ อัพโหลด
5.7 หากสามารถอัพโหลดโปรแกรมลงบอร์ดได้สำเร็จ จะแสดงคำว่า Done uploading. ที่แถบด้านล่าง
1. กดปุ่มรีเซ็ตเพื่อเปิดกล้อง T-Camera Plus ESP32
2. กล้องนับถอยหลังจาก 3
3. ปรับมุมที่คุณชื่นชอบ
4. กล้องถ่ายรูปเริ่มถ่ายภาพ 3 ภาพติดต่อกัน
5. เล่นภาพที่ถ่ายครั้งสุดท้ายโดยอัตโนมัติ
6. เข้าสู่ Sleep Mode หลังจาก 5 วินาที
7. กดปุ่มรีเซ็ตเพื่อถ่ายอีกครั้ง
7. วีดีโอ ผลลัพล์การทำงาน
credit : https://www.instructables.com/id/Arduino-Selfie-Camera/
ไม่มีความคิดเห็น:
แสดงความคิดเห็น