今回事務所から離れた場所にあるアナログメータの写真を
事務所PCより確認したいとご依頼を頂きましたので、
LoRa通信を使用してESP32で取得したカメラ映像をPCから確認できるようにしました。
まずそのためには、カメラのデータをESP32が取り込む必要があります。
パート1では、カメラ映像をSDカードに保存する処理までをやってみます。
ハードウェア
・FREENOVE ESP32-S3 cam
・マイクロSDカード
ソフトウェア
ソースコードは以下
#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カードに画像を保存します。