昨年末に購入していたESP32-P4-Picoなんだけど、Arduino core for the ESP32のバージョンが3.3.11に上がってIDF v5.5.4 with ESP-Videoとなっていたので、Arduinoでもカメラモジュールが簡単に使えるようになってる!ということで試してみた。
ESP32-P4-PicoはRaspberry Pi Zero 2Wなどと同じ、0.5mmピッチの22Pinタイプのフラットケーブルが刺さるようになっている。カメラ側は1.0mmピッチの15Pinのものが多いので変換ケーブルが必要かも。
ESP-VideoではOV5647をサポートしているので、Raspberry Pi Camera v1.3がそのまま使えそう。昔購入したラズパイカメラの互換品と、Raspberry Pi Zero用に購入したカメラ変換ケーブルを使用してテストしてみることに。
フラットケーブルはカメラによって反対だったりするので注意かも。黒い可動部分と反対側に端子面が接触するように差し込む感じ。
とりあえずテストでOV5647で写真を撮影して、ESP32-P4のハードウェアエンコーダでJPEGに変換してmicroSDに保存してみた。
#include <Arduino.h>
#include <FS.h>
#include <SD_MMC.h>
#include <ESP_Video.h>
#include "driver/jpeg_encode.h"
#define CAMERA_SCCB_PORT 0
#define CAMERA_SCCB_SCL 8
#define CAMERA_SCCB_SDA 7
#define CAMERA_I2C_FREQ 400000
#define CAMERA_BUFFERS 2
#define SD_CLK 43
#define SD_CMD 44
#define SD_D0 39
#define SD_D1 40
#define SD_D2 41
#define SD_D3 42
#define JPEG_QUALITY 80
#define JPEG_OUTPUT_BUFFER_SIZE (4UL * 1024UL * 1024UL)
#define CAPTURE_INTERVAL_MS 5000
ESPVideoClass video;
ESPVideoCaptureDevClass capture_dev;
jpeg_encoder_handle_t jpeg_encoder = nullptr;
uint8_t *jpeg_buffer = nullptr;
size_t jpeg_buffer_size = 0;
uint32_t photo_number = 1;
unsigned long last_capture = 0;
bool initSD() {
return SD_MMC.setPins(SD_CLK, SD_CMD, SD_D0, SD_D1, SD_D2, SD_D3) && SD_MMC.begin("/sdcard", false);
}
bool initCamera() {
ESPVideoCamConfigClass cam_cfg;
ESPVideoCSIConfigClass csi_cfg;
return cam_cfg.begin(CAMERA_SCCB_PORT, CAMERA_SCCB_SCL, CAMERA_SCCB_SDA, CAMERA_I2C_FREQ) &&
csi_cfg.begin(cam_cfg) &&
video.begin(csi_cfg) &&
capture_dev.begin(ESP_VIDEO_MIPI_CSI_DEVICE_NAME, CAMERA_BUFFERS) &&
capture_dev.setFormat(ESP_VIDEO_FORMAT_RGB565) &&
capture_dev.startCapture();
}
bool initJPEG() {
jpeg_encode_engine_cfg_t eng_cfg = {.intr_priority = 0, .timeout_ms = 100};
if (jpeg_new_encoder_engine(&eng_cfg, &jpeg_encoder) != ESP_OK) return false;
jpeg_encode_memory_alloc_cfg_t mem_cfg = {.buffer_direction = JPEG_ENC_ALLOC_OUTPUT_BUFFER};
jpeg_buffer = (uint8_t *)jpeg_alloc_encoder_mem(JPEG_OUTPUT_BUFFER_SIZE, &mem_cfg, &jpeg_buffer_size);
if (!jpeg_buffer) {
jpeg_del_encoder_engine(jpeg_encoder);
jpeg_encoder = nullptr;
return false;
}
return true;
}
String nextFilename() {
char filename[32];
while (true) {
snprintf(filename, sizeof(filename), "/photo_%04lu.jpg", (unsigned long)photo_number);
if (!SD_MMC.exists(filename)) {
photo_number = (photo_number >= 999999) ? 1 : photo_number + 1;
return String(filename);
}
photo_number = (photo_number >= 999999) ? 1 : photo_number + 1;
}
}
bool capturePhoto() {
if (!capture_dev.isOpened() || !capture_dev.isCaptureStarted()) return false;
ESPVideoBufferClass buffer = capture_dev.captureBuffer();
if (!buffer.valid() || buffer.formatType() != ESP_VIDEO_FORMAT_RGB565) return false;
uint32_t width = buffer.getWidth();
uint32_t height = buffer.getHeight();
size_t input_size = buffer.size();
uint8_t *input = buffer.data();
if (!input || width == 0 || height == 0) return false;
jpeg_encode_cfg_t enc_cfg = {};
enc_cfg.src_type = JPEG_ENCODE_IN_FORMAT_RGB565;
enc_cfg.sub_sample = JPEG_DOWN_SAMPLING_YUV420;
enc_cfg.image_quality = JPEG_QUALITY;
enc_cfg.width = width;
enc_cfg.height = height;
enc_cfg.pixel_reverse = false;
uint32_t jpeg_size = 0;
unsigned long start = millis();
if (jpeg_encoder_process(jpeg_encoder, &enc_cfg, input, (uint32_t)input_size,
jpeg_buffer, (uint32_t)jpeg_buffer_size, &jpeg_size) != ESP_OK) {
return false;
}
unsigned long elapsed = millis() - start;
String filename = nextFilename();
File file = SD_MMC.open(filename.c_str(), FILE_WRITE);
if (!file) return false;
size_t written = file.write(jpeg_buffer, jpeg_size);
file.close();
if (written == jpeg_size) {
Serial.printf("Saved: %s (%lu bytes, %lu ms)\n", filename.c_str(), (unsigned long)jpeg_size, elapsed);
return true;
}
return false;
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\n=== SYSTEM INIT ===");
#ifdef BOARD_HAS_PSRAM
Serial.printf("PSRAM: ENABLED (%u / %u bytes free)\n",
(unsigned int)ESP.getFreePsram(), (unsigned int)ESP.getPsramSize());
#endif
if (!initSD() || !initCamera() || !initJPEG()) {
Serial.println("SYSTEM ERROR: Init failed");
return;
}
Serial.println("SYSTEM READY");
last_capture = millis() - CAPTURE_INTERVAL_MS;
}
void loop() {
if (!capture_dev.isOpened() || !capture_dev.isCaptureStarted()) {
delay(1000);
return;
}
if (millis() - last_capture >= CAPTURE_INTERVAL_MS) {
last_capture = millis();
capturePhoto();
#ifdef BOARD_HAS_PSRAM
Serial.printf("Free PSRAM: %u bytes\n", (unsigned int)ESP.getFreePsram());
#endif
}
delay(10);
}Arduino coreは3.3.11を使用。それ以下だとESP_Video.hが見つからない。ビルド設定はデフォルトからPSRAMはEnableにしただけ。もし新しいリビジョンのESP32-P4を使っている場合はChip Variantをv3.00 or Newerにしないとうまく動かないかも。
とりあえずシリアルコンソールを確認してみたらちゃんと動いてそう。適当なタイミングで電源を切ってmicroSDの中身を確認してみるとちゃんとjpgファイルがたくさん生成されていて写真が取れてる。
解像度は800x800で動いてるみたいだけどどうやらドライバーのデフォルト値?どうやって解像度を変更できるのかESP_Video.hの仕様を調べる必要がありそう。
0 件のコメント:
コメントを投稿