ESP32-S3 WROOM CAMでデジカメを作る

https://qiita.com/hassy131/items/983c6fe583309b1c7454

上記の記事ではWROVERでテストしましたが仕事が遅かったのでS3でテストしました。LCDも解像度がよいST7789を使用しました。

![ESP32S3_WROOM_SD.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806156/d518462c-5d6e-ddb0-ef2a-851ea57d5eb0.png)


ESP32-S3_WROOM CAM３部作になります。
1. [ESP32-S3 WROOM CAMでデジカメを作る](https://qiita.com/drafts/dc229fbff1b81f0478ce/edit)
GPIO0ボタン押下で静止画撮影、SDカードに保存。EdgeImpulseでのトレーニングデータ収集の為。
2. SDカード画像をブラウザで確認
SDカードの取り外しが面倒なため
3. 画像識別ST7789でバウンディングボックス
EdgeImpulseでの学習モデルを元に画像識別

# １．ESP32-S3 WROOM CAMでデジカメを作る
GPIO0ボタンを押すとカメラで撮影し、その画像をSDカードにJPEG形式で保存します。また、TFTディスプレイにも画像を表示します。

## TFT_eSPI.h関連
User_Setup_Select.hの修正、下記追加する。
```c++
#include <User_Setups/Setup303_ESP32_ST7789A.h> 
```

User_Setupsフォルダ内に新設
```c++ (Setup303_ESP32_ST7789A.h)

#define USER_SETUP_ID 303

#define ST7789_DRIVER 

#define TFT_WIDTH  240
#define TFT_HEIGHT 280

//FOR ESP32-S3 WROOM
#define TFT_MOSI 14  //=SDA
#define TFT_SCLK 47
#define TFT_CS   42// Chip select control pin
#define TFT_DC   21// Data Command control pin
#define TFT_RST  41// Reset pin (could connect to RST pin)

#define LOAD_GLCD
#define LOAD_FONT2
#define LOAD_FONT4
#define LOAD_FONT6
#define LOAD_FONT7
#define LOAD_FONT8
#define LOAD_GFXFF
#define SMOOTH_FONT

#define SPI_FREQUENCY 40000000
#define SPI_READ_FREQUENCY 20000000
#define SPI_TOUCH_FREQUENCY 2500000
```

ソースコードピックアップ
## #include
```c++
#include <Arduino.h>
#include <TFT_eSPI.h>  // TFTディスプレイ制御用ライブラリ
#include <SPI.h>       // SPI通信のライブラリ
#include "esp_camera.h" // ESP32カメラ機能を操作するライブラリ
#include "FS.h"         // ファイルシステム管理用ライブラリ
#include "SD_MMC.h"     // SDカード操作用ライブラリ (SDMMC)
```

## setup()
```c++
// セットアップ関数
void setup() {
    Serial.begin(115200); 

    pinMode(BUTTON_PIN, INPUT_PULLUP);  // GPIO0をプルアップで入力設定

    // カメラの設定
    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_RGB565; 
    config.frame_size = FRAMESIZE_240X240;  
    config.grab_mode = CAMERA_GRAB_WHEN_EMPTY; 
    config.fb_location = CAMERA_FB_IN_PSRAM; 
    config.jpeg_quality = 12;  // JPEG圧縮品質を設定
    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);
        return;
    }
    Serial.println("Camera ready");  // カメラの初期化成功を通知
    camera_sign = true;  // カメラの初期化成功を示すフラグを設定

    // カメラセンサーの設定を取得
    sensor_t * s = esp_camera_sensor_get();
    if (s != NULL) {
        s->set_hmirror(s, 1); // 水平反転を有効にする、でないとLCDに鏡の様に左右反転する為
 
    }

    tft.init();  // TFTディスプレイを初期化
    tft.setRotation(1);  // ディスプレイの回転を設定
    tft.fillScreen(TFT_WHITE);  // 画面全体を白で塗りつぶす

    // SDカードの初期化
    initSDCard();
}

```

## main()
```c++
void loop() {
    if (sd_sign && camera_sign) {  // SDカードとカメラが初期化されている場合
        camera_fb_t *fb = esp_camera_fb_get();  // カメラのフレームバッファを取得
        if (!fb) {  // フレーム取得に失敗した場合
            Serial.println("Failed to get camera frame buffer");
            return;
        }
        
        if (button_is_pressed()) {  // ボタンが押された場合
            Serial.println("Button is pressed");
            char filename[32];  // ファイル名用のバッファ
            sprintf(filename, "/s3_image%d.jpg", imageCount);  // ファイル名を生成
            Serial.printf("Saving picture to: %s\n", filename);
            writeJpegToSD(filename, fb);  // JPEG形式で画像をSDカードに保存
            Serial.println("Picture saved.");
            imageCount++;  // ファイル番号を更新
        }

        uint8_t* buf = fb->buf;  // フレームバッファのデータ
        uint32_t len = fb->len;  // フレームバッファのデータ長

        Serial.println("Displaying image on TFT...");  
        tft.startWrite();  // TFTディスプレイへの書き込みを開始
        tft.setAddrWindow(0, 0, camera_width, camera_height);  // 表示領域を設定
        tft.pushColors(buf, len);  // 画像データをディスプレイに表示
        tft.endWrite();  // 書き込みを終了
        Serial.println("Image displayed on TFT.");
        
        esp_camera_fb_return(fb);  // フレームバッファを解放
        delay(10);  // 少し待機
    }
}

```

## 結果

![CameraZOOM-20240918153545858 (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806156/321f1ca6-a213-48ab-09ca-cd07f023ba09.jpeg)
![CameraZOOM-20240918153604809 (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806156/bbb5d6a5-7750-6380-5e2a-c5fc68e6e829.jpeg)

![20240918_153614.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806156/2d4aef3d-f7ab-b217-b416-b27cf8f2e069.gif)


続編はこちら↓になります。

https://qiita.com/hassy131/items/45f3e266983a01c2cfc1