pdf-icon

Arduino入門

2. デバイス&サンプル

6. アプリケーション

Unit CamS3-5MP Web CAM

このサンプルプログラムは、Unit CamS3-5MP を使用して HTTP Web CAM を実現し、LAN 内での映像プレビューを可能にします。

サンプルプログラム

コンパイル要件

コンパイルバージョン要件
Unit CamS3-5MP には 2 種類の内蔵ファームウェアバージョンが存在します。使用前に Unit CamS3-5MP クイックスタート を参照し、バージョン情報を取得して、実際に使用するバージョンに応じたコンパイル環境を設定してください。

書き込み時に Wi-Fi 情報を設定する必要があります。ファームウェア書き込み後、シリアルモニタでデバイスの IP アドレスを確認し、アクセスしてください。

現在のデモはデフォルトでピクセル解像度 FRAMESIZE_VGA(640x480) を使用しています。他の解像度に変更する場合は、以下の設定を参考にしてください。高解像度では画像データサイズが大きくなるため、転送速度が遅くなります。

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
#include <WiFi.h>
#include "esp_camera.h"

const char* ssid     = "ssid";
const char* password = "password";

WiFiServer server(80);

static void jpegStream(WiFiClient* client);

#define PWDN_GPIO_NUM  -1
#define RESET_GPIO_NUM 21
#define XCLK_GPIO_NUM  11
#define SIOD_GPIO_NUM  17
#define SIOC_GPIO_NUM  41

#define Y9_GPIO_NUM    13
#define Y8_GPIO_NUM    4
#define Y7_GPIO_NUM    10
#define Y6_GPIO_NUM    5
#define Y5_GPIO_NUM    7
#define Y4_GPIO_NUM    16
#define Y3_GPIO_NUM    15
#define Y2_GPIO_NUM    6
#define VSYNC_GPIO_NUM 42
#define HREF_GPIO_NUM  18
#define PCLK_GPIO_NUM  12
#define LED_GPIO_NUM   14

void setup()
{
    Serial.begin(115200);
    Serial.setDebugOutput(true);
    Serial.println();

    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_sccb_sda = SIOD_GPIO_NUM;
    config.pin_sccb_scl = SIOC_GPIO_NUM;
    config.pin_pwdn     = PWDN_GPIO_NUM;
    config.pin_reset    = RESET_GPIO_NUM;
    config.xclk_freq_hz = 20000000;
    config.frame_size   = FRAMESIZE_VGA;
    config.pixel_format = PIXFORMAT_JPEG;
    config.grab_mode    = CAMERA_GRAB_WHEN_EMPTY;
    config.fb_location  = CAMERA_FB_IN_PSRAM;
    config.jpeg_quality = 14;
    config.fb_count     = 1;

    // カメラ初期化
    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();
    // 初期状態のセンサーは縦方向に反転しており、色がやや鮮やか
    if (s->id.PID == OV3660_PID) {
        s->set_vflip(s, 1);        // 元に戻す
        s->set_brightness(s, 1);   // 輝度を少し上げる
        s->set_saturation(s, -2);  // 彩度を下げる
    }

    WiFi.begin(ssid, password);
    WiFi.setSleep(false);

    Serial.print("WiFi connecting");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.println("");
    Serial.println("WiFi connected");

    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    server.begin();
}

void loop()
{
    WiFiClient client = server.available();  // クライアント接続待ち
    if (client) {                            // クライアントが接続した場合
        while (client.connected()) {         // 接続中はループ
            if (client.available()) {        // 受信データがある場合
                jpegStream(&client);
            }
        }
        // 接続終了
        client.stop();
        Serial.println("Client Disconnected.");
    }
}

#define PART_BOUNDARY "123456789000000000000987654321"
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY     = "--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART         = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";

static void jpegStream(WiFiClient* client)
{
    Serial.println("Image stream satrt");
    client->println("HTTP/1.1 200 OK");
    client->printf("Content-Type: %s\r\n", _STREAM_CONTENT_TYPE);
    client->println("Content-Disposition: inline; filename=capture.jpg");
    client->println("Access-Control-Allow-Origin: *");
    client->println();
    static int64_t last_frame = 0;
    if (!last_frame) {
        last_frame = esp_timer_get_time();
    }
    camera_fb_t* fb;

    for (;;) {
        fb = esp_camera_fb_get();
        if (!fb) {
            delay(10);
            continue;
        }

        Serial.printf("pic size: %d\n", fb->len);
        client->print(_STREAM_BOUNDARY);
        client->printf(_STREAM_PART, fb->len);
        int32_t to_sends    = fb->len;
        int32_t now_sends   = 0;
        uint8_t* out_buf    = fb->buf;
        uint32_t packet_len = 8 * 1024;
        while (to_sends > 0) {
            now_sends = to_sends > packet_len ? packet_len : to_sends;
            if (client->write(out_buf, now_sends) == 0) {
                goto client_exit;
            }
            out_buf += now_sends;
            to_sends -= now_sends;
        }

        int64_t fr_end     = esp_timer_get_time();
        int64_t frame_time = fr_end - last_frame;
        last_frame         = fr_end;
        frame_time /= 1000;
        Serial.printf("MJPG: %luKB %lums (%.1ffps)\r\n", (long unsigned int)(fb->len / 1024),
                      (long unsigned int)frame_time, 1000.0 / (long unsigned int)frame_time);
        esp_camera_fb_return(fb);
    }
client_exit:
    if (fb) {
        esp_camera_fb_return(fb);
    }
    client->stop();
    Serial.printf("Image stream end\r\n");
}

ピクセル切替

5MP 解像度を使用する場合は、カメラ初期化設定を以下のように変更してください。

config.frame_size   = FRAMESIZE_5MP;
config.jpeg_quality = 63;
On This Page