본문 바로가기
IoT

ESP32 카메라 모듈로 무선통신 스트리밍 서버만들기

by 비너발트 2025. 5. 31.

 

IoT 관련하여 프로젝트를 만들다 보니 다양한 기기에 대한 호기심이 생겨 하나씩 만들어 보고 있습니다

마이크로컨트롤러에선 원래 다량의 데이터를 주고받는 게 아니라 제어와 관련된 역할을 주로 담당하는데 저렴하게 카메라테스트를 해볼 수 있는 정도선에서 구현이 가능하여 테스트를 해보았습니다

 

 

1. 카메라 모델 및 핀 설정하기

#define CAMERA_MODEL_AI_THINKER
#include "camera_pins.h"

 

CAMERA_MODEL_AI_THINKER를 미리 정의하면 camera_pins.h 내부에서 AI Thinker 보드에 맞는 GPIO 번호들이 매크로로 설정됩니다.

 

2. 필수 라이브러리 포함하기

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

 

WiFi.h: ESP32의 무선 LAN 연결

esp_camera.h: 카메라 모듈 초기화·제어

esp_http_server.h: HTTP 서버(라우팅·스트리밍) 기능

 

3. 네트워크 자격 증명

const char* ssid = "와이파이 이름";
const char* password = "패스워드";

 

ESP32가 접속할 Wi-Fi SSID와 비밀번호를 지정 이후만들 setup()에서 WiFi.begin(ssid, password)로 부팅 시 자동으로 연결 시도

 

4. setup() 함수

void setup() {
  Serial.begin(115200); //시리얼 통신 디버깅 메세지 115200 baud로 출력하도록
  Serial.setDebugOutput(false);

  WiFi.begin(ssid, password); // 와이파이 접속시도
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected"); // 와파 연결
  Serial.println(WiFi.localIP()); // 아이피 출력

  // 카메라 설정
  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_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;

  if (psramFound()) {
    config.frame_size = FRAMESIZE_QVGA; // 해상도
    config.jpeg_quality = 10; //화질
    config.fb_count = 2; //버퍼
  } else {
    config.frame_size = FRAMESIZE_QVGA;
    config.jpeg_quality = 12;
    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;
  }

  startCameraServer(); // 스트리밍 서버 함수 호출
}

 

 

5. startCameraServer() 함수

 

void startCameraServer() {
  httpd_config_t config = HTTPD_DEFAULT_CONFIG();
  config.server_port = 80;

  httpd_handle_t server = NULL;  // 서버 핸들러

  httpd_uri_t index_uri = {
    .uri = "/",
    .method = HTTP_GET,
    .handler = [](httpd_req_t *req) {
      const char* html = R"rawliteral(
        <html>
          <head><title>간이 cctv 만들기</title></head>
          <body>
            <h2>ESP32-CAM</h2>
            <img src="/stream" />
          </body>
        </html>
      )rawliteral";
      httpd_resp_send(req, html, HTTPD_RESP_USE_STRLEN);
      return ESP_OK;
    }
  };

  httpd_uri_t stream_uri = {
    .uri = "/stream",
    .method = HTTP_GET,
    .handler = [](httpd_req_t *req) {
      camera_fb_t * fb = NULL;
      esp_err_t res = ESP_OK;
      char * part_buf[64];

      res = httpd_resp_set_type(req, "multipart/x-mixed-replace; boundary=frame");
      if (res != ESP_OK) return res;

      while (true) {
        fb = esp_camera_fb_get();
        if (!fb) {
          Serial.println("Camera capture failed");
          continue;
        }

        snprintf((char *)part_buf, 64, "--frame\r\nContent-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n", fb->len);
        res = httpd_resp_send_chunk(req, (const char *)part_buf, strlen((char *)part_buf));
        if (res == ESP_OK) res = httpd_resp_send_chunk(req, (const char *)fb->buf, fb->len);
        if (res == ESP_OK) res = httpd_resp_send_chunk(req, "\r\n", 2);

        esp_camera_fb_return(fb);
        if (res != ESP_OK) break;
        delay(50);
      }
      return res;
    }
  };

  if (httpd_start(&server, &config) == ESP_OK) {  // server 사용
    httpd_register_uri_handler(server, &index_uri);
    httpd_register_uri_handler(server, &stream_uri);
    Serial.println("Camera stream ready at /stream");
  } else {
    Serial.println( Failed to start web server");
  }
}

 

간단히 말하자면 jpg로 사진을 연속촬영해서 연속된 사진을 계속 교체하여 넘기면서 보여주는 방식으로 보여주게됩니다

이렇게 간단하게 간이 CCTV를 만들어보았는데 ESP 성능이 좋지않기 때문에 테스트 및 학습용도로 적합합니다 아무리 최적화를 잘한다 하여도 주기적으로 재부팅되거나 멈추는 경우가 잦기때문에 테스트 용도로만 권장드립니다 물론 재부팅되면 자동으로 와이파이와 서버구동이 되기때문에 지속성에선 큰 문제가 되지않습니다😊

 

다음엔 고성능 프로세서를 이용해 찐 CCTV를 만들어볼 예정입니다