カメラモジュールOV2640の使い方(ESP-WROOM-32)

# 概要
ESP-WROOM-32を使ってカメラ―モジュールOV2640を制御してみました。いくつか引っかかった点があるので記載しておきます。
回路は下記を参考にしました。
https://github.com/igrr/esp32-cam-demo

OV2640
![DSC_0418.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426480/f911d1bd-09ff-2bb3-7f06-f9b656d2d367.jpeg)

# データシート一覧

Register Listなど載っている
http://akizukidenshi.com/download/ds/omnivision/ov2640.pdf

Application Note
http://www.dragonwake.com/download/camera/ov2640/OV2640%20Camera%20Module%20Software%20Application%20Notes.pdf

# 参考記事

一番元となる資料
https://github.com/igrr/esp32-cam-demo

XCLK 20Mhz→6MHzへ変更したところ正常に動作
http://t3ta.hatenablog.com/entry/2018/01/16/211352

I2CとSCCBの違い　スレーブアドレスが0x60でSCCBの場合：0b01100000(write) 、0b01100001(read)
https://qiita.com/Rohira/items/07874504a43f0b07cdd9
http://marsee101.blog19.fc2.com/blog-entry-1322.html

ESP-WROOM-32 ピン配置
https://ht-deko.com/arduino/pic/esp-wroom-32_pinout_01.png

公式
https://github.com/espressif/esp32-camera

# まずはSCCB通信まで

1. XCLKにPWM信号を出力（カメラモジュールのクロックになる）
2. リセットピンをLOW→HIGH(active lowのため)
3. 0x1Dレジスタを読み込むと、0xA2であれば正常に通信できています。

```c:app_main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"

#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "esp_err.h"
#include "nvs_flash.h"

#include "driver/ledc.h"
#include "driver/i2c.h"

#define TAG "CAMERA_TEST"

/********* i2c ***********/
#define I2C_CAMERA_SCL_IO 23
#define I2C_CAMERA_SDA_IO 25
#define I2C_CAMERA_NUM I2C_NUM_0
#define I2C_CAMERA_TX_BUF_DISABLE 0
#define I2C_CAMERA_RX_BUF_DISABLE 0
#define I2C_CAMERA_FREQ_HZ 100000
#define ACK_CHECK_EN 0x1
#define ACK_CHECK_DIS 0x0
#define ACK_VAL 0x0
#define NACK_VAL 0x1

/********* camera ***********/
#define CAMERA_IO_RESET 15
#define CAMERA_IO_XCLK 27
#define CAMERA_XCLK_FREQ 6000000

#define CAMERA_PID_H 0x0A
#define CAMERA_PID_L 0x0B
#define CAMERA_MID_H 0x1C
#define CAMERA_MID_L 0x1D

/********* gpio ***********/
#define GPIO_OUTPUT_PIN_SEL  (1<<CAMERA_IO_RESET)

void init_i2c(){
	int i2c_master_port = I2C_NUM_0;
	i2c_config_t conf;
	conf.mode = I2C_MODE_MASTER;
	conf.sda_io_num = I2C_CAMERA_SDA_IO;
	conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
	conf.scl_io_num = I2C_CAMERA_SCL_IO;
	conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
	conf.master.clk_speed = I2C_CAMERA_FREQ_HZ;
	i2c_param_config(i2c_master_port, &conf);
	i2c_driver_install(i2c_master_port, conf.mode, I2C_CAMERA_RX_BUF_DISABLE, I2C_CAMERA_TX_BUF_DISABLE, 0);
}

esp_err_t init_camera_clock(){
	periph_module_enable(PERIPH_LEDC_MODULE);

	ledc_timer_config_t timer_conf = {
		.duty_resolution = 1,
		.freq_hz = CAMERA_XCLK_FREQ,
		.speed_mode = LEDC_HIGH_SPEED_MODE,
		.timer_num = LEDC_TIMER_0
	};
	esp_err_t err = ledc_timer_config(&timer_conf);
	if(err != ESP_OK){
		ESP_LOGE(TAG, "ledc_timer_config failed, err=%d", err);
		return err;
	}

	ledc_channel_config_t ch_conf = {
		.channel = LEDC_CHANNEL_0,
		.timer_sel = LEDC_TIMER_0,
		.intr_type = LEDC_INTR_DISABLE,
		.duty = 1,
		.speed_mode = LEDC_HIGH_SPEED_MODE,
		.gpio_num = CAMERA_IO_XCLK
	};
	err = ledc_channel_config(&ch_conf);
	if(err != ESP_OK){
		ESP_LOGE(TAG, "ledc_channel_config failed, err=%d", err);
		return err;
	}

	return ESP_OK;
}

esp_err_t read_camera_config(uint8_t* camera_mid_h, uint8_t reg){

	i2c_cmd_handle_t cmd = i2c_cmd_link_create();
	i2c_master_start(cmd);
	i2c_master_write_byte(cmd, 0b01100000, ACK_CHECK_EN);
	i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
	i2c_master_stop(cmd);
	esp_err_t err = i2c_master_cmd_begin(I2C_CAMERA_NUM, cmd, 10 / portTICK_RATE_MS);
	i2c_cmd_link_delete(cmd);

	if(err != ESP_OK){
		ESP_LOGE(TAG, "read_camera_config 1 failed, err=%d", err);
		return err;
	}

	cmd = i2c_cmd_link_create();
	i2c_master_start(cmd);
	i2c_master_write_byte(cmd, 0b01100001, ACK_CHECK_EN);
	i2c_master_read_byte(cmd, camera_mid_h, NACK_VAL);
	i2c_master_stop(cmd);
	err = i2c_master_cmd_begin(I2C_CAMERA_NUM, cmd, 10 / portTICK_RATE_MS);
	i2c_cmd_link_delete(cmd);

	if(err != ESP_OK){
		ESP_LOGE(TAG, "read_camera_config 2 failed, err=%d", err);
		return err;
	}

    return ESP_OK;
}

void app_main(){

	esp_log_level_set("*", ESP_LOG_VERBOSE);

    esp_err_t err = nvs_flash_init();
    if(err != ESP_OK){
        ESP_ERROR_CHECK(nvs_flash_erase());
        ESP_ERROR_CHECK(nvs_flash_init());
    }

	//GPIO設定
	gpio_config_t io_conf;
	io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
	io_conf.mode = GPIO_MODE_OUTPUT;
	io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
	io_conf.pull_down_en = 0;
	io_conf.pull_up_en = 0;
	gpio_config(&io_conf);

	//CAMERAクロック出力
	init_camera_clock();

	//I2C初期化
	init_i2c();

	//CAMERAリセット 「RESET PIN = active low」
	gpio_set_level(CAMERA_IO_RESET, 0);
	vTaskDelay(10 / portTICK_RATE_MS);
	gpio_set_level(CAMERA_IO_RESET, 1);
	vTaskDelay(10 / portTICK_RATE_MS);

	//CAMERA接続確認
	uint8_t camera_pid[2], camera_mid[2];

	read_camera_config(&camera_mid[0], CAMERA_MID_L);
	ESP_LOGI(TAG, "app_main camera_mid_l=0x%X", camera_mid[0]); //0xA2であればOK
	if(camera_mid[0] != 0xA2){
		ESP_LOGE(TAG, "app_main failed, camera_mid_l=%X", camera_mid[0]);
		return;
	}

	read_camera_config(&camera_mid[1], CAMERA_MID_H);
	ESP_LOGI(TAG, "app_main camera_mid_h=0x%X", camera_mid[1]); //0x7FであればOK
	if(camera_mid[1] != 0x7F){
		ESP_LOGE(TAG, "app_main failed, camera_mid_h=%X", camera_mid[1]);
		return;
	}

	read_camera_config(&camera_pid[0], CAMERA_PID_L);
	ESP_LOGI(TAG, "app_main camera_pid_l=0x%X", camera_pid[0]);

	read_camera_config(&camera_pid[1], CAMERA_PID_H);
	ESP_LOGI(TAG, "app_main camera_pid_h=0x%X", camera_pid[1]);

	//処理終了
	ESP_LOGI(TAG, "FINISH");
	return;
}

```

# 画像取得
下記を参考に設定部分とDMA処理をできるだけ分けました。ただ、サイズ変更の際はSCCBでの設定及びDMAどちらにも影響するので設定変更する必要があるのでお忘れなく！
https://github.com/igrr/esp32-cam-demo

全ファイルはgitにアップしてます！
https://github.com/koki-iwaizumi/esp32-ov2640/blob/master/main/app_main.c

```c:app_main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"

#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "esp_err.h"
#include "nvs_flash.h"

#include "driver/i2c.h"
#include "driver/gpio.h"
#include "camera.h"
#include "http_server.h"

/********* config ***********/
#define WIFI_SSID "*"
#define WIFI_PASSWORD "*"

/********* i2c ***********/
#define I2C_CAMERA_TX_BUF_DISABLE 0
#define I2C_CAMERA_RX_BUF_DISABLE 0
#define I2C_CAMERA_FREQ_HZ 100000
#define I2C_CAMERA_NUM I2C_NUM_0
#define ACK_CHECK_EN 0x1
#define ACK_CHECK_DIS 0x0
#define ACK_VAL 0x0
#define NACK_VAL 0x1

#define CAMERA_SLAVE_ADDRESS_READ 0b01100001
#define CAMERA_SLAVE_ADDRESS_WRITE 0b01100000

/********* gpio ***********/
#define PIN_D0 35
#define PIN_D1 17
#define PIN_D2 34
#define PIN_D3 5
#define PIN_D4 39
#define PIN_D5 18
#define PIN_D6 36
#define PIN_D7 19
#define PIN_XCLK 27
#define PIN_PCLK 21
#define PIN_VSYNC 22
#define PIN_HREF 26
#define PIN_SDA 25
#define PIN_SCL 23
#define PIN_RESET 15

#define GPIO_OUTPUT_PIN_SEL  (1 << PIN_RESET)

static const char* TAG = "camera_demo app_main";

static EventGroupHandle_t s_wifi_event_group;
const int CONNECTED_BIT = BIT0;
static ip4_addr_t s_ip_addr;

/********* camera reg ***********/
static const uint8_t default_regs[][2] = {
	{0xFF, 0x00},
	{0x2C, 0xFF},
	{0x2E, 0xDF},

	{0xFF, 0x01},
	{0x3C, 0x32},
	{0x11, 0x80},
	{0x09, 0x02},
	{0x28, 0x00},
	{0x13, 0xE5},
	{0x14, 0x48},
	{0x15, 0x00},
	{0x2C, 0x0C},
	{0x33, 0x78},
	{0x3A, 0x33},
	{0x3B, 0xFB},
	{0x3E, 0x00},
	{0x43, 0x11},
	{0x16, 0x10},
	{0x39, 0x02},
	{0x35, 0x88},
	{0x22, 0x0A},
	{0x37, 0x40},
	{0x23, 0x00},
	{0x34, 0xA0},
	{0x06, 0x02},
	{0x06, 0x88},
	{0x07, 0xC0},
	{0x0D, 0xB7},
	{0x0E, 0x01},
	{0x4C, 0x00},
	{0x4A, 0x81},
	{0x21, 0x99},
	{0x24, 0x40},
	{0x25, 0x38},
	{0x26, 0x82},
	{0x48, 0x00},
	{0x49, 0x00},
	{0x5C, 0x00},
	{0x63, 0x00},
	{0x46, 0x00},
	{0x47, 0x00},
	{0x0C, 0x3A},
	{0x5D, 0x55},
	{0x5E, 0x7D},
	{0x5F, 0x7D},
	{0x60, 0x55},
	{0x61, 0x70},
	{0x62, 0x80},
	{0x7C, 0x05},
	{0x20, 0x80},
	{0x28, 0x30},
	{0x6C, 0x00},
	{0x6D, 0x80},
	{0x6E, 0x00},
	{0x70, 0x02},
	{0x71, 0x94},
	{0x73, 0xC1},
	{0x3D, 0x34},
	{0x5A, 0x57},
	{0x4F, 0xBB},
	{0x50, 0x9C},

	{0xFF, 0x00},
	{0xE5, 0x7F},
	{0xF9, 0xC0},
	{0x41, 0x24},
	{0xE0, 0x14},
	{0x76, 0xFF},
	{0x33, 0xA0},
	{0x42, 0x20},
	{0x43, 0x18},
	{0x4C, 0x00},
	{0x87, 0xD0},
	{0x88, 0x3F},
	{0xD7, 0x03},
	{0xD9, 0x10},
	{0xD3, 0x82},
	{0xC8, 0x08},
	{0xC9, 0x80},
	{0x7C, 0x00},
	{0x7D, 0x00},
	{0x7C, 0x03},
	{0x7D, 0x48},
	{0x7D, 0x48},
	{0x7C, 0x08},
	{0x7D, 0x20},
	{0x7D, 0x10},
	{0x7D, 0x0E},
	{0x90, 0x00},
	{0x91, 0x0E},
	{0x91, 0x1A},
	{0x91, 0x31},
	{0x91, 0x5A},
	{0x91, 0x69},
	{0x91, 0x75},
	{0x91, 0x7E},
	{0x91, 0x88},
	{0x91, 0x8F},
	{0x91, 0x96},
	{0x91, 0xA3},
	{0x91, 0xAF},
	{0x91, 0xC4},
	{0x91, 0xD7},
	{0x91, 0xE8},
	{0x91, 0x20},
	{0x92, 0x00},
	{0x93, 0x06},
	{0x93, 0xE3},
	{0x93, 0x03},
	{0x93, 0x03},
	{0x93, 0x00},
	{0x93, 0x02},
	{0x93, 0x00},
	{0x93, 0x00},
	{0x93, 0x00},
	{0x93, 0x00},
	{0x93, 0x00},
	{0x93, 0x00},
	{0x93, 0x00},
	{0x96, 0x00},
	{0x97, 0x08},
	{0x97, 0x19},
	{0x97, 0x02},
	{0x97, 0x0C},
	{0x97, 0x24},
	{0x97, 0x30},
	{0x97, 0x28},
	{0x97, 0x26},
	{0x97, 0x02},
	{0x97, 0x98},
	{0x97, 0x80},
	{0x97, 0x00},
	{0x97, 0x00},
	{0xA4, 0x00},
	{0xA8, 0x00},
	{0xC5, 0x11},
	{0xC6, 0x51},
	{0xBF, 0x80},
	{0xC7, 0x10},
	{0xB6, 0x66},
	{0xB8, 0xA5},
	{0xB7, 0x64},
	{0xB9, 0x7C},
	{0xB3, 0xAF},
	{0xB4, 0x97},
	{0xB5, 0xFF},
	{0xB0, 0xC5},
	{0xB1, 0x94},
	{0xB2, 0x0F},
	{0xC4, 0x5C},
	{0xA6, 0x00},
	{0xA7, 0x20},
	{0xA7, 0xD8},
	{0xA7, 0x1B},
	{0xA7, 0x31},
	{0xA7, 0x00},
	{0xA7, 0x18},
	{0xA7, 0x20},
	{0xA7, 0xD8},
	{0xA7, 0x19},
	{0xA7, 0x31},
	{0xA7, 0x00},
	{0xA7, 0x18},
	{0xA7, 0x20},
	{0xA7, 0xD8},
	{0xA7, 0x19},
	{0xA7, 0x31},
	{0xA7, 0x00},
	{0xA7, 0x18},
	{0x7F, 0x00},
	{0xE5, 0x1F},
	{0xE1, 0x77},
	{0xDD, 0x7F},
	{0xC2, 0x0E},

	{0x00, 0x00}
};

static const uint8_t svga_regs[][2] = {
	{0xFF, 0x01},
	{0x12, 0x40},
	{0x03, 0x0F},
	{0x32, 0x09},
	{0x17, 0x11},
	{0x18, 0x43},
	{0x19, 0x00},
	{0x1A, 0x4B},
	{0x3D, 0x38},
	{0x35, 0xDA},
	{0x22, 0x1A},
	{0x37, 0xC3},
	{0x34, 0xC0},
	{0x06, 0x88},
	{0x0D, 0x87},
	{0x0E, 0x41},
	{0x42, 0x03},

	{0xFF, 0x00},
	{0x05, 0x01},
	{0xE0, 0x04},
	{0xC0, 0x64},
	{0xC1, 0x4B},
	{0x8C, 0x00},
	{0x53, 0x00},
	{0x54, 0x00},
	{0x51, 0xC8},
	{0x52, 0x96},
	{0x55, 0x00},
	{0x57, 0x00},
	{0x86, 0x3D},
	{0x50, 0x80},
	{0xD3, 0x80},
	{0x05, 0x00},
	{0xE0, 0x00},

	{0x00, 0x00}
};

static const uint8_t jpeg_regs[][2] = {
	{0xFF, 0x00},
	{0xE0, 0x04},
	{0xDA, 0x18},
	{0xD7, 0x03},
	{0xE1, 0x77},
	{0x44, 0x0C},
	{0xE0, 0x00},

	{0x00, 0x00}
};

static const uint8_t framesize_low_regs[][2] = {
	{0xFF, 0x00},
	{0x05, 0x01},
	{0x5A, 0xA0},
	{0x5B, 0x78},
	{0x5C, 0x00},

	{0xFF, 0x01},
	{0x11, 0x83},

	{0x00, 0x00}
};

static const uint8_t framesize_high_regs[][2] = {
	{0xFF, 0x00},
	{0x05, 0x00},

	{0x00, 0x00}
};

static const uint8_t quality_regs[][2] = {
	{0xFF, 0x00},
	{0x44, 0x0F},

	{0x00, 0x00}
};

esp_err_t read_camera_config(uint8_t reg, uint8_t* data){

	i2c_cmd_handle_t cmd = i2c_cmd_link_create();
	i2c_master_start(cmd);
	i2c_master_write_byte(cmd, CAMERA_SLAVE_ADDRESS_WRITE, ACK_CHECK_EN);
	i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
	i2c_master_stop(cmd);
	esp_err_t err = i2c_master_cmd_begin(I2C_CAMERA_NUM, cmd, 10 / portTICK_RATE_MS);
	i2c_cmd_link_delete(cmd);

	if(err != ESP_OK){
		return err;
	}

	cmd = i2c_cmd_link_create();
	i2c_master_start(cmd);
	i2c_master_write_byte(cmd, CAMERA_SLAVE_ADDRESS_READ, ACK_CHECK_EN);
	i2c_master_read_byte(cmd, data, NACK_VAL);
	i2c_master_stop(cmd);
	err = i2c_master_cmd_begin(I2C_CAMERA_NUM, cmd, 10 / portTICK_RATE_MS);
	i2c_cmd_link_delete(cmd);

	if(err != ESP_OK){
		return err;
	}

    return ESP_OK;
}

esp_err_t write_camera_config(uint8_t reg, uint8_t data){

	i2c_cmd_handle_t cmd = i2c_cmd_link_create();
	i2c_master_start(cmd);
	i2c_master_write_byte(cmd, CAMERA_SLAVE_ADDRESS_WRITE, ACK_CHECK_EN);
	i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
	i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
	i2c_master_stop(cmd);
	esp_err_t err = i2c_master_cmd_begin(I2C_CAMERA_NUM, cmd, 10 / portTICK_RATE_MS);
	i2c_cmd_link_delete(cmd);

	if(err != ESP_OK){
		return err;
	}

    return ESP_OK;
}

static esp_err_t write_frame(http_context_t http_ctx){

    http_buffer_t fb_data = {
		.data = camera_get_fb(),
		.size = camera_get_data_size(),
		.data_is_persistent = true
    };
    return http_response_write(http_ctx, &fb_data);
}

static void handle_jpg(http_context_t http_ctx, void* ctx){

    esp_err_t err = camera_run();
    if(err != ESP_OK){
        ESP_LOGD(TAG, "Camera capture failed with error = %d", err);
        return;
    }

    http_response_begin(http_ctx, 200, "image/jpeg", camera_get_data_size());
    http_response_set_header(http_ctx, "Content-disposition", "inline; filename=capture.jpg");
    write_frame(http_ctx);
    http_response_end(http_ctx);
}

static esp_err_t event_handler(void *ctx, system_event_t *event){

    switch(event->event_id){
        case SYSTEM_EVENT_STA_START:
            esp_wifi_connect();
            break;
        case SYSTEM_EVENT_STA_GOT_IP:
            xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
            s_ip_addr = event->event_info.got_ip.ip_info.ip;
            break;
        case SYSTEM_EVENT_STA_DISCONNECTED:
            esp_wifi_connect();
            xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
            break;
        default:
            break;
    }
    return ESP_OK;
}

static void initialise_wifi(void){

    tcpip_adapter_init();
    s_wifi_event_group = xEventGroupCreate();
    esp_event_loop_init(event_handler, NULL);
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&cfg);
    esp_wifi_set_storage(WIFI_STORAGE_RAM);
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = WIFI_SSID,
            .password = WIFI_PASSWORD,
        },
    };
    esp_wifi_set_mode(WIFI_MODE_STA);
    esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
    esp_wifi_start();
    esp_wifi_set_ps(WIFI_PS_NONE);
    ESP_LOGI(TAG, "Connecting to \"%s\"", wifi_config.sta.ssid);
    xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY);
    ESP_LOGI(TAG, "Connected");
}

esp_err_t init_camera_clock(camera_config_t* config){

	periph_module_enable(PERIPH_LEDC_MODULE);

	ledc_timer_config_t timer_conf = {
        .duty_resolution = 1,
        .freq_hz = config->xclk_freq_hz,
        .speed_mode = LEDC_HIGH_SPEED_MODE,
        .timer_num = config->ledc_timer
	};
	esp_err_t err = ledc_timer_config(&timer_conf);
	if(err != ESP_OK){
		ESP_LOGE(TAG, "ledc_timer_config failed, err=%d", err);
		return err;
	}

	ledc_channel_config_t ch_conf = {
        .channel = config->ledc_channel,
        .timer_sel = config->ledc_timer,
        .intr_type = LEDC_INTR_DISABLE,
        .duty = 1,
        .speed_mode = LEDC_HIGH_SPEED_MODE,
        .gpio_num = config->pin_xclk
	};
	err = ledc_channel_config(&ch_conf);
	if(err != ESP_OK){
		ESP_LOGE(TAG, "ledc_channel_config failed, err=%d", err);
		return err;
	}

	return ESP_OK;
}

void init_i2c(camera_config_t* config){
	int i2c_master_port = I2C_NUM_0;
	i2c_config_t conf;
	conf.mode = I2C_MODE_MASTER;
	conf.sda_io_num = config->pin_sscb_sda;
	conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
	conf.scl_io_num = config->pin_sscb_scl;
	conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
	conf.master.clk_speed = I2C_CAMERA_FREQ_HZ;
	i2c_param_config(i2c_master_port, &conf);
	i2c_driver_install(i2c_master_port, conf.mode, I2C_CAMERA_RX_BUF_DISABLE, I2C_CAMERA_TX_BUF_DISABLE, 0);
}

void app_main(){

	esp_log_level_set("*", ESP_LOG_VERBOSE);

    esp_err_t err = nvs_flash_init();
    if(err != ESP_OK){
        nvs_flash_erase();
        nvs_flash_init();
    }

    gpio_install_isr_service(0);

    camera_config_t camera_config = {
		.ledc_channel = LEDC_CHANNEL_0,
		.ledc_timer = LEDC_TIMER_0,
		.pin_d0 = PIN_D0,
		.pin_d1 = PIN_D1,
		.pin_d2 = PIN_D2,
		.pin_d3 = PIN_D3,
		.pin_d4 = PIN_D4,
		.pin_d5 = PIN_D5,
		.pin_d6 = PIN_D6,
		.pin_d7 = PIN_D7,
		.pin_xclk = PIN_XCLK,
		.pin_pclk = PIN_PCLK,
		.pin_vsync = PIN_VSYNC,
		.pin_href = PIN_HREF,
		.pin_sscb_sda = PIN_SDA,
		.pin_sscb_scl = PIN_SCL,
		.pin_reset = PIN_RESET,

		.xclk_freq_hz = 6000000,
		.width = 640,
		.height = 480,
    };

	ESP_LOGE(TAG, "xclk_freq_hz=%d[Hz]", camera_config.xclk_freq_hz);

	//GPIO設定
	gpio_config_t io_conf;
	io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
	io_conf.mode = GPIO_MODE_OUTPUT;
	io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
	io_conf.pull_down_en = 0;
	io_conf.pull_up_en = 0;
	gpio_config(&io_conf);

	//CAMERA クロック出力
	init_camera_clock(&camera_config);

	//I2C 初期化
	init_i2c(&camera_config);

	//CAMERA リセット
	gpio_set_level(PIN_RESET, 0);
	vTaskDelay(10 / portTICK_RATE_MS);
	gpio_set_level(PIN_RESET, 1);
	vTaskDelay(10 / portTICK_RATE_MS);

	//CAMERA OV2640確認
	uint8_t camera_pid_h;
	read_camera_config(0x0A, &camera_pid_h);
	ESP_LOGI(TAG, "camera_pid_h=0x%02X", camera_pid_h);
    if(camera_pid_h != 0x26){
        ESP_LOGE(TAG, "failed, camera_pid_h=%02X", camera_pid_h);
        return;
    }

	//CAMERA システムリセット
	write_camera_config(0xFF, 0x01);
	write_camera_config(0x12, 0x80);
	vTaskDelay(10 / portTICK_RATE_MS);

	//CAMERA 設定書き込み
    int i = 0;
    while(default_regs[i][0]){
		write_camera_config(default_regs[i][0], default_regs[i][1]);
        i++;
    }
	i = 0;
    while(jpeg_regs[i][0]){
		write_camera_config(jpeg_regs[i][0], jpeg_regs[i][1]);
        i++;
    }
	i = 0;
    while(framesize_low_regs[i][0]){
		write_camera_config(framesize_low_regs[i][0], framesize_low_regs[i][1]);
        i++;
    }
	i = 0;
    while(svga_regs[i][0]){
		write_camera_config(svga_regs[i][0], svga_regs[i][1]);
        i++;
    }
	i = 0;
    while(framesize_high_regs[i][0]){
		write_camera_config(framesize_high_regs[i][0], framesize_high_regs[i][1]);
        i++;
    }
	i = 0;
    while(quality_regs[i][0]){
		write_camera_config(quality_regs[i][0], quality_regs[i][1]);
        i++;
    }

	//CAMERA その他設定
    err = camera_init(&camera_config);
    if(err != ESP_OK){
        ESP_LOGE(TAG, "Camera init failed with error 0x%x", err);
        return;
    }

	//CAMERA wifi設定
    initialise_wifi();
    http_server_t server;
    http_server_options_t http_options = HTTP_SERVER_OPTIONS_DEFAULT();
    http_server_start(&http_options, &server);

	http_register_handler(server, "/jpg", HTTP_GET, HTTP_HANDLE_RESPONSE, &handle_jpg, NULL);
	ESP_LOGI(TAG, "Open http://" IPSTR "/jpg for single image/jpg image", IP2STR(&s_ip_addr));

    ESP_LOGI(TAG, "Free heap: %u", xPortGetFreeHeapSize());
    ESP_LOGI(TAG, "Camera demo ready");
}

```