IoT

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

비너발트 2025. 5. 31. 03:17

 

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를 만들어볼 예정입니다