fork download
  1. #include "esp_camera.h"
  2. #include "FS.h"
  3. #include "SD_MMC.h"
  4. #include <SPI.h>
  5. #include <TFT_eSPI.h>
  6. #include <JPEGDecoder.h>
  7.  
  8. // เซ็ตจอ TFT
  9. TFT_eSPI tft = TFT_eSPI();
  10.  
  11. // เซ็นเซอร์เหรียญและปุ่ม
  12. #define COIN_SENSOR_PIN 14
  13. #define BUTTON_PIN 15
  14.  
  15. bool takePhotoFlag = false;
  16. unsigned long lastDebounceTime = 0;
  17. const unsigned long debounceDelay = 200;
  18.  
  19. // พินกล้อง AI-Thinker
  20. #define PWDN_GPIO_NUM 32
  21. #define RESET_GPIO_NUM -1
  22. #define XCLK_GPIO_NUM 0
  23. #define SIOD_GPIO_NUM 26
  24. #define SIOC_GPIO_NUM 27
  25. #define Y9_GPIO_NUM 35
  26. #define Y8_GPIO_NUM 34
  27. #define Y7_GPIO_NUM 39
  28. #define Y6_GPIO_NUM 36
  29. #define Y5_GPIO_NUM 21
  30. #define Y4_GPIO_NUM 19
  31. #define Y3_GPIO_NUM 18
  32. #define Y2_GPIO_NUM 5
  33. #define VSYNC_GPIO_NUM 25
  34. #define HREF_GPIO_NUM 23
  35. #define PCLK_GPIO_NUM 22
  36.  
  37. // ตัวแปรเก็บรายชื่อไฟล์
  38. #define MAX_IMAGES 30
  39. String imageFiles[MAX_IMAGES];
  40. int imageCount = 0;
  41. int currentImage = 0;
  42.  
  43. // ฟังก์ชันประกาศกล้อง
  44. void initCamera() {
  45. camera_config_t config;
  46. config.ledc_channel = LEDC_CHANNEL_0;
  47. config.ledc_timer = LEDC_TIMER_0;
  48. config.pin_d0 = Y2_GPIO_NUM;
  49. config.pin_d1 = Y3_GPIO_NUM;
  50. config.pin_d2 = Y4_GPIO_NUM;
  51. config.pin_d3 = Y5_GPIO_NUM;
  52. config.pin_d4 = Y6_GPIO_NUM;
  53. config.pin_d5 = Y7_GPIO_NUM;
  54. config.pin_d6 = Y8_GPIO_NUM;
  55. config.pin_d7 = Y9_GPIO_NUM;
  56. config.pin_xclk = XCLK_GPIO_NUM;
  57. config.pin_pclk = PCLK_GPIO_NUM;
  58. config.pin_vsync = VSYNC_GPIO_NUM;
  59. config.pin_href = HREF_GPIO_NUM;
  60. config.pin_sscb_sda = SIOD_GPIO_NUM;
  61. config.pin_sscb_scl = SIOC_GPIO_NUM;
  62. config.pin_pwdn = PWDN_GPIO_NUM;
  63. config.pin_reset = RESET_GPIO_NUM;
  64. config.xclk_freq_hz = 20000000;
  65. config.pixel_format = PIXFORMAT_JPEG;
  66. config.frame_size = FRAMESIZE_VGA; // 640x480
  67. config.jpeg_quality = 12; // คุณภาพ 10-20 ปรับได้
  68. config.fb_count = 2;
  69.  
  70. esp_err_t err = esp_camera_init(&config);
  71. if (err != ESP_OK) {
  72. Serial.printf("Camera init failed with error 0x%x\n", err);
  73. while(true);
  74. }
  75. }
  76.  
  77. // อ่านชื่อไฟล์ .jpg ใน SD card
  78. void scanImages() {
  79. File root = SD_MMC.open("/");
  80. imageCount = 0;
  81. while(true) {
  82. File entry = root.openNextFile();
  83. if (!entry) break;
  84. String fname = entry.name();
  85. if (fname.endsWith(".jpg") || fname.endsWith(".JPG")) {
  86. if (imageCount < MAX_IMAGES) {
  87. imageFiles[imageCount++] = fname;
  88. Serial.println("Found image: " + fname);
  89. }
  90. }
  91. entry.close();
  92. }
  93. }
  94.  
  95. void drawJpegFile(File jpegFile, int x, int y) {
  96. if (JpegDec.decodeSdFile(jpegFile)) {
  97. uint16_t *pImg = JpegDec.pImage;
  98. uint16_t mcu_w = JpegDec.MCUWidth;
  99. uint16_t mcu_h = JpegDec.MCUHeight;
  100. uint16_t max_x = x + JpegDec.width;
  101. uint16_t max_y = y + JpegDec.height;
  102.  
  103. for (uint16_t j=0; j < JpegDec.MCUSPerCol; j++) {
  104. for (uint16_t i=0; i < JpegDec.MCUSPerRow; i++) {
  105. for (uint16_t y_pos=0; y_pos < mcu_h; y_pos++) {
  106. tft.pushColors(&pImg[(j * mcu_h + y_pos) * mcu_w * JpegDec.MCUSPerRow + i * mcu_w], mcu_w);
  107. }
  108. }
  109. }
  110. } else {
  111. Serial.println("JPEG decode failed");
  112. }
  113. }
  114.  
  115. void setup() {
  116. Serial.begin(115200);
  117.  
  118. pinMode(COIN_SENSOR_PIN, INPUT);
  119. pinMode(BUTTON_PIN, INPUT_PULLUP);
  120.  
  121. tft.init();
  122. tft.setRotation(1);
  123. tft.fillScreen(TFT_BLACK);
  124.  
  125. if(!SD_MMC.begin()){
  126. Serial.println("SD Card Mount Failed");
  127. while(true);
  128. }
  129.  
  130. initCamera();
  131.  
  132. scanImages();
  133.  
  134. Serial.printf("Total images: %d\n", imageCount);
  135. }
  136.  
  137. void loop() {
  138. // อ่านเซ็นเซอร์เหรียญ (Active LOW สมมติ)
  139. if(digitalRead(COIN_SENSOR_PIN) == LOW || digitalRead(BUTTON_PIN) == LOW) {
  140. unsigned long now = millis();
  141. if(now - lastDebounceTime > debounceDelay) {
  142. takePhotoFlag = true;
  143. lastDebounceTime = now;
  144. }
  145. }
  146.  
  147. if(takePhotoFlag) {
  148. takePhotoFlag = false;
  149.  
  150. // ถ่ายภาพ
  151. camera_fb_t * fb = esp_camera_fb_get();
  152. if(!fb) {
  153. Serial.println("Camera capture failed");
  154. return;
  155. }
  156.  
  157. // สร้างชื่อไฟล์จากเวลาปัจจุบัน
  158. String path = "/img_" + String(millis()) + ".jpg";
  159.  
  160. File file = SD_MMC.open(path.c_str(), FILE_WRITE);
  161. if(!file) {
  162. Serial.println("Failed to open file for writing");
  163. } else {
  164. file.write(fb->buf, fb->len);
  165. Serial.println("Saved: " + path);
  166. file.close();
  167.  
  168. // เพิ่มภาพใหม่ในอาเรย์
  169. if(imageCount < MAX_IMAGES) {
  170. imageFiles[imageCount++] = path;
  171. }
  172. }
  173.  
  174. esp_camera_fb_return(fb);
  175. }
  176.  
  177. // แสดงภาพวนลูป
  178. if(imageCount > 0) {
  179. File imgFile = SD_MMC.open(imageFiles[currentImage].c_str());
  180. if(imgFile) {
  181. tft.fillScreen(TFT_BLACK);
  182. drawJpegFile(imgFile, 0, 0);
  183. imgFile.close();
  184.  
  185. currentImage++;
  186. if(currentImage >= imageCount) currentImage = 0;
  187. }
  188. delay(5000); // แสดงภาพละ 5 วินาที
  189. } else {
  190. tft.fillScreen(TFT_BLACK);
  191. tft.setCursor(10, 30);
  192. tft.setTextColor(TFT_WHITE);
  193. tft.setTextSize(2);
  194. tft.println("No Images");
  195. delay(2000);
  196. }
  197. }
Success #stdin #stdout 0.03s 25888KB
stdin
Standard input is empty
stdout
#include "esp_camera.h"
#include "FS.h"
#include "SD_MMC.h"
#include <SPI.h>
#include <TFT_eSPI.h>
#include <JPEGDecoder.h>

// เซ็ตจอ TFT
TFT_eSPI tft = TFT_eSPI();

// เซ็นเซอร์เหรียญและปุ่ม
#define COIN_SENSOR_PIN 14
#define BUTTON_PIN 15

bool takePhotoFlag = false;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 200;

// พินกล้อง 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

// ตัวแปรเก็บรายชื่อไฟล์
#define MAX_IMAGES 30
String imageFiles[MAX_IMAGES];
int imageCount = 0;
int currentImage = 0;

// ฟังก์ชันประกาศกล้อง
void initCamera() {
  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;
  config.frame_size = FRAMESIZE_VGA; // 640x480
  config.jpeg_quality = 12; // คุณภาพ 10-20 ปรับได้
  config.fb_count = 2;

  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x\n", err);
    while(true);
  }
}

// อ่านชื่อไฟล์ .jpg ใน SD card
void scanImages() {
  File root = SD_MMC.open("/");
  imageCount = 0;
  while(true) {
    File entry = root.openNextFile();
    if (!entry) break;
    String fname = entry.name();
    if (fname.endsWith(".jpg") || fname.endsWith(".JPG")) {
      if (imageCount < MAX_IMAGES) {
        imageFiles[imageCount++] = fname;
        Serial.println("Found image: " + fname);
      }
    }
    entry.close();
  }
}

void drawJpegFile(File jpegFile, int x, int y) {
  if (JpegDec.decodeSdFile(jpegFile)) {
    uint16_t *pImg = JpegDec.pImage;
    uint16_t mcu_w = JpegDec.MCUWidth;
    uint16_t mcu_h = JpegDec.MCUHeight;
    uint16_t max_x = x + JpegDec.width;
    uint16_t max_y = y + JpegDec.height;

    for (uint16_t j=0; j < JpegDec.MCUSPerCol; j++) {
      for (uint16_t i=0; i < JpegDec.MCUSPerRow; i++) {
        for (uint16_t y_pos=0; y_pos < mcu_h; y_pos++) {
          tft.pushColors(&pImg[(j * mcu_h + y_pos) * mcu_w * JpegDec.MCUSPerRow + i * mcu_w], mcu_w);
        }
      }
    }
  } else {
    Serial.println("JPEG decode failed");
  }
}

void setup() {
  Serial.begin(115200);

  pinMode(COIN_SENSOR_PIN, INPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);

  tft.init();
  tft.setRotation(1);
  tft.fillScreen(TFT_BLACK);

  if(!SD_MMC.begin()){
    Serial.println("SD Card Mount Failed");
    while(true);
  }

  initCamera();

  scanImages();

  Serial.printf("Total images: %d\n", imageCount);
}

void loop() {
  // อ่านเซ็นเซอร์เหรียญ (Active LOW สมมติ)
  if(digitalRead(COIN_SENSOR_PIN) == LOW || digitalRead(BUTTON_PIN) == LOW) {
    unsigned long now = millis();
    if(now - lastDebounceTime > debounceDelay) {
      takePhotoFlag = true;
      lastDebounceTime = now;
    }
  }

  if(takePhotoFlag) {
    takePhotoFlag = false;

    // ถ่ายภาพ
    camera_fb_t * fb = esp_camera_fb_get();
    if(!fb) {
      Serial.println("Camera capture failed");
      return;
    }

    // สร้างชื่อไฟล์จากเวลาปัจจุบัน
    String path = "/img_" + String(millis()) + ".jpg";

    File file = SD_MMC.open(path.c_str(), FILE_WRITE);
    if(!file) {
      Serial.println("Failed to open file for writing");
    } else {
      file.write(fb->buf, fb->len);
      Serial.println("Saved: " + path);
      file.close();

      // เพิ่มภาพใหม่ในอาเรย์
      if(imageCount < MAX_IMAGES) {
        imageFiles[imageCount++] = path;
      }
    }

    esp_camera_fb_return(fb);
  }

  // แสดงภาพวนลูป
  if(imageCount > 0) {
    File imgFile = SD_MMC.open(imageFiles[currentImage].c_str());
    if(imgFile) {
      tft.fillScreen(TFT_BLACK);
      drawJpegFile(imgFile, 0, 0);
      imgFile.close();

      currentImage++;
      if(currentImage >= imageCount) currentImage = 0;
    }
    delay(5000); // แสดงภาพละ 5 วินาที
  } else {
    tft.fillScreen(TFT_BLACK);
    tft.setCursor(10, 30);
    tft.setTextColor(TFT_WHITE);
    tft.setTextSize(2);
    tft.println("No Images");
    delay(2000);
  }
}