1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

# 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 // フレームバッファ数(2つあれば2倍で処理できる?)
    };  
    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() {
}
1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?