2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

今回事務所から離れた場所にあるアナログメータの写真を
事務所PCより確認したいとご依頼を頂きましたので、
LoRa通信を使用してESP32で取得したカメラ映像をPCから確認できるようにしました。

image.png

まずそのためには、カメラのデータをESP32が取り込む必要があります。
パート1では、カメラ映像をSDカードに保存する処理までをやってみます。

ハードウェア

・FREENOVE ESP32-S3 cam
・マイクロSDカード

ソフトウェア

まずは、書込み設定です。
image.png

ソースコードは以下

#include "esp_camera.h"
#include "SD_MMC.h"

// Pin definition for CAMERA_MODEL_ESP32S3_EYE
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 15
#define SIOD_GPIO_NUM 4
#define SIOC_GPIO_NUM 5

#define Y2_GPIO_NUM 11
#define Y3_GPIO_NUM 9
#define Y4_GPIO_NUM 8
#define Y5_GPIO_NUM 10
#define Y6_GPIO_NUM 12
#define Y7_GPIO_NUM 18
#define Y8_GPIO_NUM 17
#define Y9_GPIO_NUM 16

#define VSYNC_GPIO_NUM 6
#define HREF_GPIO_NUM 7
#define PCLK_GPIO_NUM 13

// Pin definition for SD MMC
#define SD_MMC_CMD 38  //Please do not modify it.
#define SD_MMC_CLK 39  //Please do not modify it.
#define SD_MMC_D0 40   //Please do not modify it.

// カメラ画像の更新間隔 (ミリ秒で指定) 500ミリ秒間隔
#define UPDATE_INTERVAL (500u)

// カメラ画像フレームバッファ
camera_fb_t* fb;

void cameraInit(void) {
  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;  // 画像のフォーマット YUV422,GRAYSCALE,RGB565,JPEGから選択

  //PSRAMが存在する場合、UXGA解像度と高いJPEG画質で開始し、
  //事前に割り当てられたフレームバッファを大きくする。
  if (psramFound()) {
    Serial.println("psramFound true");
    config.jpeg_quality = 10;               //10-63 数字が小さいほど高品質
    config.fb_count = 2;                    //バッファ数選択
    config.frame_size = FRAMESIZE_VGA;      // FRAMESIZE_ + QVGA(320×240)|CIF(352×288)|VGA(640×480)|SVGA(800×600)|XGA(1024×768)|SXGA(1280×1024)|UXGA(1600×1200)
    config.grab_mode = CAMERA_GRAB_LATEST;  //バッファに保存するタイミングを設定 CAMERA_GRAB_WHEN_EMPTY=前回データ、CAMERA_GRAB_LATEST=最新のデータ
    config.fb_location = CAMERA_FB_IN_PSRAM;
  } else {
    // PSRAMが使用できない場合、フレームサイズを制限する。
    Serial.println("psramFound false");
    config.jpeg_quality = 20;
    config.fb_count = 1;
    config.frame_size = FRAMESIZE_VGA;
    config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
    config.fb_location = CAMERA_FB_IN_DRAM;
  }

  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }

  sensor_t* s = esp_camera_sensor_get();
  s->set_brightness(s, 0);                  // -2 to 2
  s->set_contrast(s, 0);                    // -2 to 2
  s->set_saturation(s, 0);                  // -2 to 2
  s->set_special_effect(s, 0);              // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
  s->set_whitebal(s, 1);                    // 0 = disable , 1 = enable
  s->set_awb_gain(s, 1);                    // 0 = disable , 1 = enable
  s->set_wb_mode(s, 0);                     // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
  s->set_exposure_ctrl(s, 1);               // 0 = disable , 1 = enable
  s->set_aec2(s, 0);                        // 0 = disable , 1 = enable
  s->set_ae_level(s, 0);                    // -2 to 2
  s->set_aec_value(s, 300);                 // 0 to 1200
  s->set_gain_ctrl(s, 1);                   // 0 = disable , 1 = enable
  s->set_agc_gain(s, 0);                    // 0 to 30
  s->set_gainceiling(s, (gainceiling_t)0);  // 0 to 6
  s->set_bpc(s, 0);                         // 0 = disable , 1 = enable
  s->set_wpc(s, 1);                         // 0 = disable , 1 = enable
  s->set_raw_gma(s, 1);                     // 0 = disable , 1 = enable
  s->set_lenc(s, 1);                        // 0 = disable , 1 = enable
  s->set_hmirror(s, 0);                     // 0 = disable , 1 = enable
  s->set_vflip(s, 0);                       // 0 = disable , 1 = enable
  s->set_dcw(s, 1);                         // 0 = disable , 1 = enable
  s->set_colorbar(s, 0);                    // 0 = disable , 1 = enable
  s->set_denoise(s, 1);                     // ノイズ除去
}

void sdmmcInit(void) {
  SD_MMC.setPins(SD_MMC_CLK, SD_MMC_CMD, SD_MMC_D0);
  if (!SD_MMC.begin("/sdcard", true, true, SDMMC_FREQ_DEFAULT, 5)) {
    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);
  Serial.printf("Total space: %lluMB\r\n", SD_MMC.totalBytes() / (1024 * 1024));
  Serial.printf("Used space: %lluMB\r\n", SD_MMC.usedBytes() / (1024 * 1024));
}

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");
  }
}

int readFileNum(fs::FS& fs, const char* dirname) {
  File root = fs.open(dirname);
  if (!root) {
    Serial.println("Failed to open directory");
    return -1;
  }
  if (!root.isDirectory()) {
    Serial.println("Not a directory");
    return -1;
  }

  File file = root.openNextFile();
  int num = 0;
  while (file) {
    file = root.openNextFile();
    num++;
  }
  return num;
}

void writejpg(fs::FS& fs, const char* path, const uint8_t* buf, size_t size) {
  File file = fs.open(path, FILE_WRITE);
  if (!file) {
    Serial.println("Failed to open file for writing");
    return;
  }
  file.write(buf, size);
  Serial.printf("Saved file to path: %s\r\n", path);
}

void printBuffer(byte* buf, size_t size) {
  for (size_t i = 0; i < size; i++) {
    Serial.print(buf[i], HEX);  // 16進数で表示
    Serial.print(" ");
  }
  Serial.println();
}

void setup() {
  Serial.begin(115200);
  delay(100);
  Serial.println("Setup Start");

  // カメラ初期化
  cameraInit();
  // カメラ画像が正常に撮れるか確認
  fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
    // カメラ画像が取得できない場合は再起動
    ESP.restart();
  }

  // フレームバッファ解放
  esp_camera_fb_return(fb);

  // SDカード初期化
  sdmmcInit();
  createDir(SD_MMC, "/camera");

  // カメラのフレームを取得
  fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
    // カメラ画像が取得できない場合は停止
    for (;;)
      yield();
  }
  Serial.println("Taking picture..");

  if (fb != NULL) {
    int photo_index = readFileNum(SD_MMC, "/camera");
    if (photo_index != -1) {
      Serial.println("camera shutter");

      String path = "/camera/" + String(photo_index) + ".jpg";
      // 画像保存
      writejpg(SD_MMC, path.c_str(), fb->buf, fb->len);
      Serial.println(fb->len);
      // バッファの内容を表示
      printBuffer(fb->buf, fb->len);
    }
  }
}

void loop() {
}

起動時にカメラ撮影を行いSDカードに画像を保存します。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?