M5Cameraに直接接続して映像を見る

```
#include <WiFi.h>
#include <esp_http_server.h>
#include <esp_camera.h>

// カメラに接続するときのSSID（周りの人とかぶらないものにすること）
#define CAMERA_SSID "my_camera"

// 1: 上下反転する, 0: しない
#define FLIP 1

// 区切り用のランダム文字列（変更不要）
#define BOUNDARY_KEY  "123456789000000000000987654321"

void setup() {

    // シリアル通信開始
    Serial.begin(115200);
    Serial.setDebugOutput(true);
    Serial.println("power on");

    // WiFi アクセスポイントモード設定
    Serial.println("WiFi ap mode");
    WiFi.mode(WIFI_MODE_AP);
    WiFi.softAP(CAMERA_SSID);
    Serial.println("Please Connect SSID " CAMERA_SSID " and Browse http://192.168.4.1/");

    // カメラ接続
    Serial.println("camera begin");
    camera_config_t config = {
        .pin_pwdn = -1,
        .pin_reset = 15,
        .pin_xclk = 27,
        .pin_sscb_sda = 22,
        .pin_sscb_scl = 23,
        .pin_d7 = 19,
        .pin_d6 = 36,
        .pin_d5 = 18,
        .pin_d4 = 39,
        .pin_d3 = 5,
        .pin_d2 = 34,
        .pin_d1 = 35,
        .pin_d0 = 32,
        .pin_vsync = 25,
        .pin_href = 26,
        .pin_pclk = 21,
        .xclk_freq_hz = 20000000, // 20MHz
        .ledc_timer = LEDC_TIMER_0, // 0番のタイマー使用
        .ledc_channel = LEDC_CHANNEL_0, // 0番のチャネル使用
        .pixel_format = PIXFORMAT_JPEG, // JPEG
        .frame_size = FRAMESIZE_VGA, // 解像度
        .jpeg_quality = 12, // JPGE画質（小さいほど高画質）
        .fb_count = 1 // フレームバッファ数（２つあれば２倍で処理できる？）
    };  
    esp_err_t result = esp_camera_init(&config);
    if (result != ESP_OK) {
        Serial.printf("esp_camera_init error 0x%x\n", result);
        return;
    }

    // 上下反転設定
    sensor_t * sensor = esp_camera_sensor_get();
    sensor->set_vflip(sensor, FLIP);

    // HTTP Server 開始
    Serial.println("httpd begin");
    httpd_handle_t httpd = NULL;
    httpd_config_t httpd_config = HTTPD_DEFAULT_CONFIG();
    Serial.printf("lisen port %d\n", httpd_config.server_port);
    result = httpd_start(&httpd, &httpd_config);
    if (result != ESP_OK) {
        Serial.printf("httpd_start error 0x%x\n", result);
        return;
    }

    // 映像送信処理登録
    httpd_uri_t stream = {
        .uri       = "/",
        .method    = HTTP_GET,
        .handler   = [](httpd_req_t *request) {
            Serial.println("GET /");

            // レスポンスの種類を指定
            esp_err_t result = httpd_resp_set_type(request, "multipart/x-mixed-replace;boundary=" BOUNDARY_KEY);
            if (result != ESP_OK) {
                Serial.printf("httpd_resp_set_type(multipart) error 0x%x\n", result);
            }

            // 繰り返し画像を送信（動画のように見える）
            while (result == ESP_OK) {

                //撮影
                struct Shot {

                    // カメラフレーム
                    camera_fb_t* frame;

                    // カメラ画像取得
                    Shot() : frame(esp_camera_fb_get()) {
                        if (!ok()) {
                        Serial.println("esp_camera_fb_get failed");
                        }
                    }

                    // 取得成功かどうか
                    inline bool ok() {
                        return frame != NULL;
                    }
                    // バイナリデータ
                    inline uint8_t* data() {
                        return frame->buf;
                    }
                    
                    // バイト数
                    inline size_t size() {
                        return frame->len;
                    }

                    // リソース開放
                    ~Shot() {
                        if (frame != NULL) {
                        esp_camera_fb_return(frame);
                        }
                    }
                } shot;
                if (!shot.ok()) break;

                // ヘッダ部分の送信
                char buffer[64];
                size_t length = snprintf(buffer, sizeof(buffer), "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n", shot.size());
                result = httpd_resp_send_chunk(request, buffer, length);
                if (result != ESP_OK) {
                Serial.printf("httpd_resp_send_chunk(header) error 0x%x\n", result);
                    continue;
                }

                // JPEGの送信
                result = httpd_resp_send_chunk(request, (char*)shot.data(), shot.size());
                if (result != ESP_OK) {
                Serial.printf("httpd_resp_send_chunk(image) error 0x%x\n", result);
                    continue;
                }

                // 区切り送信
                result = httpd_resp_send_chunk(request, "\r\n--" BOUNDARY_KEY "\r\n", -1);
                if (result != ESP_OK) {
                Serial.printf("httpd_resp_send_chunk(boundary) error 0x%x\n", result);
                    continue;
                }
            }
            if (result != ESP_OK) {
                Serial.printf("stream_handler error 0x%x\n", result);
                return httpd_resp_send_500(request);
            }
            return httpd_resp_send(request, NULL, 0);
        },
        .user_ctx  = NULL
    };
    result = httpd_register_uri_handler(httpd, &stream);
    if (result != ESP_OK) {
        Serial.printf("httpd_register_uri_handler(stream) error 0x%x\n", result);
        return;
    }
}

void loop() {
}
```