본문 바로가기
ESP32

ESP32-CAM으로 셀카 및 타임랩스 촬영 프로젝트 📸✨

by 모빌리티키즈 2024. 12. 12.
728x90
반응형

안녕하세요, 여러분! 이번 포스트에서는 ESP32-CAM 마이크로 컨트롤러 보드를 셀카 및 타임랩스 사진 촬영기로 변환하는 재미있는 프로젝트를 소개해 드리려고 합니다. ESP32-CAM 또는 LILYGO T-Camera와 같은 유사한 보드를 사용하여 Android 휴대폰으로 관리하는 스냅샷 촬영기를 만들어 보세요.

프로젝트 개요

이 프로젝트는 안드로이드 폰을 사용하여 ESP32-CAM에 연결하고, 스냅샷 촬영 빈도를 설정한 후, 셀카나 타임랩스 사진을 촬영하고 저장하는 과정을 포함합니다. 정말 흥미로운 DIY 프로젝트가 될 것입니다!

(가상의 이미지입니다)

준비물

  • ESP32-CAM 마이크로 컨트롤러 보드
  • Android 휴대폰
  • VSCode 및 PlatformIO: Arduino 스케치를 실행하는 데 사용됩니다.
  • DumbDisplay Android 앱: 원격 UI(제어판)를 구현합니다.
  • Bluetooth 또는 WiFi 모듈: ESP32-CAM과 Android 휴대폰 간의 연결을 위해 사용됩니다.

단계별 가이드

  1. ESP32-CAM 설정:
    • ESP32-CAM을 준비하고 전원을 연결합니다.
    • VSCode와 PlatformIO를 사용하여 Arduino 스케치를 작성하고 업로드합니다. (VSCode PlatformIO로 Arduino 스케치를 실행하는 방법 게시물을 참고하세요)
  2. DumbDisplay 앱 설치:
    • Android 휴대폰에 DumbDisplay 앱을 설치합니다.
    • 앱을 통해 ESP32-CAM과의 연결을 설정합니다. Bluetooth 또는 WiFi 중 하나를 선택할 수 있습니다.
  3. UI 설정 및 테스트:
    • DumbDisplay 앱을 통해 원격 제어판을 구성합니다.
    • UI의 흐름은 Arduino 스케치에 의해 프로그래밍됩니다. UI 설정은 스케치 코드에서 정의됩니다.
  4. 사진 촬영 및 저장:
    • 앱에서 스냅샷 촬영 빈도를 설정합니다.
    • 설정한 빈도에 따라 ESP32-CAM이 셀카 및 타임랩스 사진을 촬영합니다.
    • 촬영이 완료되면 ESP32-CAM에 다시 연결하여 촬영한 사진을 Android 휴대폰으로 전송합니다.

1 단계 : UI - 스냅 샷을 찍는 방법

 

스냅샷을 캡처하여 휴대폰에 저장할 수 있는 3가지 방법이 있습니다

  1. Bluetooth 또는 WiFi가 있는 DumbDisplay Android 앱을 통해 휴대폰을 ESP32-CAM에 연결한 상태에서 UI의 저장 버튼을 클릭하여 💾표시되는 이미지를 저장합니다. 자동❎ / 자동☑️을 클릭하여 자동 저장 기능을 켜거나 끌 수 있습니다. 자동 저장을 활성화하면 ESP32-CAM에서 스냅샷을 캡처할 때마다 휴대폰에 저장됩니다
  2. 휴대 전화를 ESP32-CAM과 연결했다고 가정하면 이미지 캔버스를 클릭하면 스냅 샷 표시가 일시 중지되고 이전에 캡처 한 스냅 샷을 선택하여 휴대 전화에 저장할 수 있도록 시간을 뒤로 슬라이드 할 수있는 슬라이더가 제공됩니다. 시간을 거슬러 올라갈 수 있는 20개의 스냅샷이 있습니다. 원하는 스냅샷이 보이면 Save(저장)를 클릭하여 💾저장합니다. 완료되면 취소를 클릭하여 ❌뒤로 돌아갈 수 있습니다(또는 이미지 캔버스를 두 번 클릭).
  3. "오프라인" 캡처 및 스냅샷을 ESP32-CAM에 저장할 수 있도록 하려면 오프라인📴을 선택합니다. "오프라인"을 활성화한 상태에서 연결을 끊으면(즉, 오프라인) ESP32-CAM이 "오프라인" 스냅샷 캡처를 시작하여 플래시 메모리(또는 SD 카드)에 저장합니다. 다시 연결할 때마다 저장된 "오프라인" 스냅샷을 휴대폰으로 전송할 것인지 묻는 메시지가 표시됩니다. 더 좋은 점은 SD 카드 케이스에서 SD 카드를 컴퓨터에 삽입하고 SD 카드의 JPEG 파일을 복사 및 삭제하는 일반적인 방법으로 저장된 "오프라인" 스냅샷 JPEG 파일을 물리적으로 전송할 수 있다는 것입니다. ESP32-CAM의 SD 카드 슬롯 사용에 대한 한 가지 포인트 -- ESP32-CAM의 SD 카드 모듈은 손전등에서 사용하는 것과 동일한 핀 4를 공유하기 때문에 SD 카드에 액세스할 때마다 손전등이 밝게 켜집니다(따라서 기능이 아니라 있는 그대로)

스냅샷은 JPEG 형식으로 저장됩니다.

  1. 휴대 전화에 저장된 스냅 샷은 20240904_223249_001.jpg (YYYYMMDD-hhmmss_seq.jpg)와 같은 파일 이름으로 DumbDisplay 앱의 개인 폴더 (/<main-storage>/Android/data/nobody.trevorlee.dumbdisplay/files/Pictures/DumbDisplay/snaps/)에 저장됩니다.
  2. 휴대폰으로 전송된 오프라인 스냅샷은 위에서 언급한 DumbDisplay 앱의 개인 폴더의 하위 폴더에 저장됩니다. 하위 폴더의 이름은 스냅샷 전송을 수행하는 시간에 따라 다르며 20240904_231329_OFF와 같습니다.
  3. 오프라인 스냅 샷은 off_001.jpg와 같은 이름으로 ESP32-CAM의 플래시 메모리 / SD 카드에 저장됩니다. "오프라인" JPEG 파일은 휴대폰에 연결한 후 ESP32-CAM을 재설정/재부팅하지 않은 경우에만 타임스탬프가 제대로 찍힙니다.

기본적으로 DumbDisplay 앱의 저장소는 DumbDisplay 앱이 초기화해야 하는 개인 폴더입니다. DumbDisplay 앱의 설정 메뉴 항목을 선택하고 미디어 저장 버튼을 클릭하면 됩니다

그리고 Files by Marc apps & software와 같은 폴더 관리자 앱을 사용하여 해당 폴더로 이동할 수 있습니다

2단계: UI -- 스냅샷 캡처 빈도

실제로 스냅 샷 캡처는 연속적이지만 (가능한 한 빠름) 캡처 된 스냅 샷 전송이 휴대 전화로 전송되는 것은 원활하지 않습니다. 실제로 스냅샷이 휴대폰으로 배송되는 빈도에 대한 설정이 있습니다. 스냅샷의 해상도/품질 및 연결 방법/조건에 따라 초당 5개의 스냅샷까지 빈도가 발생할 수 있습니다. 이 주파수 컨트롤을 원하는 주파수😁로 타임랩스 사진을 촬영할 수 있는 기능으로 취급하십시오

주파수/프레임 속도는 4가지 빠른 선택이 있습니다 -- 초당 5 프레임 / 초당 2 프레임 / 초당 1 프레임 / 분당 30 프레임에 해당하는 5 PS / 2 PS / 1 PS / 30 PM

그리고 설정하고 선택할 수 있는 사용자 지정 시간당 프레임 속도가 있습니다 -- 720 PH(720이 기본값임). 720 PH 선택을 클릭하면 원하는 값(1 - 3600)을 입력할 수 있는 휴대폰의 가상 키워드가 나타납니다(빈 값은 이전에 설정한 값을 의미함).

3단계: UI -- 스냅샷 품질 조정

캡처된 스냅샷의 품질에 영향을 주는 몇 가지 카메라 조정이 있습니다.

  1. 스냅샷 해상도/크기 선택:
  2. QVGA(320x240)
  3. VGA(640x480)
  4. SVGA(800x600)
  5. XGA (1024x768)
  6. HD (1280x720)의 HD
  7. SXGA(1280x1024)
  8. UXGA(1600x1200)
  9. JPEG 압축 품질 슬라이더 🖼️ -- 5(고품질)에서 60(저품질)까지
  10. 밝기 슬라이더 ☀️ -- -2(어둡게)에서 2(밝게)까지

스냅샷 품질/해상도가 높을수록 ESP32-CAM에서 휴대폰으로 더 많은 데이터를 전송해야 하므로 특히 WiFi가 전이중이 아니기 때문에 WiFi(Bluetooth 아님)인 경우 UI의 응답이 느려집니다.

4 단계 : UI - ESP32 유휴 절전 모드

또 다른 기능은 "오프라인" 스냅샷 캡처가 활성화되면 유휴 상태(즉, 연결되지 않음) 잠시 후(60초) ESP32-CAM이 전원을 절약하기 위해 절전 모드로 전환된다는 것입니다. 물론 "오프라인" 캡처가 활성화되어 있기 때문에 ESP32-CAM은 스냅샷을 찍기 위해 제 시간에 깨어납니다. 그러나 "오프라인" 주파수가 분당 12프레임보다 너무 높으면 ESP32-CAM이 계속 켜져 있습니다.

어쨌든 절전 모드에 있을 때 ESP32-CAM에 연결하려면 먼저 ESP32-CAM을 재설정/재부팅해야 합니다

5단계: 개발 및 구축

앞서 언급했듯이 스케치는 VS Code 및 PlatformIO를 사용하여 개발됩니다. PlatformIO 프로젝트 ESP32CamSnapper GitHub 저장소를 복제하십시오.

스케치 개발 및 구축을 위한 구성은 기본적으로 platformio.ini 파일에 기록되어 있습니다

[env]
monitor_speed = 115200
 
[env:ESP32CAM]
...
build_flags = -D FOR_ESP32CAM
 
[env:TCAMERA] ; v7
...
build_flags = -D FOR_TCAMERA
 
[env:TCAMERAPLUS]
...
build_flags = -D FOR_TCAMERAPLUS
 

올바른 PlatformIO 프로젝트 환경(ESP32CAM / TCAMERA / TCAMERAPLUS)을 선택했는지 확인하십시오.

프로그램 진입점은 src/main.cpp입니다.

// *** use Bluetooth with the following device name
#define BLUETOOTH "ESP32CamSnapper"
#include "esp32camsnapper/esp32camsnapper.ino"

위의 main.cpp에서는 Bluetooth(클래식)를 사용하는 것을 선호한다고 가정하고 Bluetooth 디바이스 이름을 ESP32CamSnapper로 정의합니다

#define BLUETOOTH "ESP32CamSnapper"
 

이 스케치의 목적을 위해 Bluetooth(클래식)는 DumbDisplay 앱과 연결하는 데 권장되는 방법입니다.

그러나 WiFi 사용을 선호하는 경우 BLUETOOTH가 아닌 WIFI_SSID와 WIFI_PASSWORD를 정의해야 합니다

//#define BLUETOOTH "ESP32CamSnapper"
#define WIFI_SSID "your-wifi-ssid"
#define WIFI_PASSWORD "your-wifi-password"
 

WiFi의 경우 ESP32-CAM에 연결하려면 ESP32-CAM의 IP를 찾아야 합니다. 이 작업은 직렬 모니터(전송 속도 115200으로 설정)에 연결하여 쉽게 수행할 수 있습니다. 거기에서 ESP32-CAM이 DumbDisplay 앱에 연결을 시도하면 아래와 같은 로그 줄이 인쇄됩니다.

binded WIFI TrevorWireless
listening on 192.168.0.155:10201 ...
listening on 192.168.0.155:10201 ...
listening on 192.168.0.155:10201 ...
 

해당 IP 주소는 연결할 ESP32-CAM의 IP 주소입니다.

ESP32-CAM(또는 LILYGO T-Camera Plus)용 SD 카드가 설치되어 있는 경우 마이크로 컨트롤러 보드의 제한된 플래시 메모리 대신 "오프라인" 스냅샷을 저장하는 데 사용할 수 있습니다.

이러한 설정에 대해 스케치를 구성하려면 스케치의 해당 선의 주석 처리를 제거하는 것과 같은 매크로 OFFLINE_USE_SD 정의해야 합니다

// *** uncomment the following if offline snaps are to be stored to SD (ESP32CAM / TCameraPlus), rather than LittleFS
#define OFFLINE_USE_SD
 

또는 #define 줄을 main.cpp 넣으십시오.

#define OFFLINE_USE_SD
#define BLUETOOTH "ESP32CamSnapper"
#include "esp32camsnapper/esp32camsnapper.ino"

6 단계 : 스케치

프로젝트의 스케치는 esp32camsnapper/esp32camsnapper.ino입니다. 다음과 같은 몇 가지 사용자 지정을 수행할 수 있습니다.

  1. 기본적으로 시간을 거슬러 올라갈 수 있는 20개의 스냅샷이 있습니다. 이 숫자는 매크로 STREAM_KEEP_IMAGE_COUNT에 의해 제어됩니다.
 
// *** maximum number of images to keep cached (on app side) when streaming snaps to DD app; this number affects how much you can go back to select which image to save
#define STREAM_KEEP_IMAGE_COUNT 20
 
  1. 기본적으로 "오프라인" 스냅샷 캡처를 활성화할 수 있습니다. "오프라인"을 전혀 원하지 않으면 해당 #define 주석 처리 할 수 있습니다
// *** support offline snap taking; comment out if not desired
#define SUPPORT_OFFLINE
 
  1. 기본적으로 ESP32-CAM은 60초 동안 유휴 상태(즉, 연결되지 않음) 후에 절전 모드로 전환됩니다. 매크로 IDLE_SLEEP_SECS 변경하여 다른 값으로 변경할 수 있습니다. 이 '유휴 상태로 전환' 동작이 바람직하지 않은 경우 해당 #define 주석으로 처리하여 이러한 동작을 비활성화할 수 있습니다
// *** number of seconds to put ESP to sleep when idle (not connected); if ESP went to sleep, will need to reset it in order to connect; comment out if not desired
#define IDLE_SLEEP_SECS 60
 
  1. UI에서 변경하는 대부분의 설정은 ESP32-CAM의 EEPROM에 유지됩니다. 이러한 설정은 빌드한 프로그램을 다시 업로드하는 경우에도 유효합니다. 이러한 설정을 기본값으로 재설정하려면 HEADER를 다른 값으로 변경하고 빌드한 후 다시 업로드하십시오
// *** if want to reset settings / offline snap metadata saved in EEPROM, set the following to something else
const int32_t HEADER = 20240910;
 

 

7단계: 스케치 하이라이트 -- EEPROM

먼저, EEPROM을 사용하기 위해서는 먼저 다음과 같이 일정 수의 바이트를 예약해야 합니다: void setup() { Serial.begin(115200); EEPROM.begin(32); ... } 여기서 32바이트는 목적을 위해 예약되어 있습니다.

대부분의 설정은 다음과 같이 ESP32-CAM의 EEPROM에 유지됩니다.

EEPROM.writeLong(0, HEADER);
EEPROM.writeChar(4, cameraBrightness);
EEPROM.writeBool(5, cameraVFlip);
EEPROM.writeBool(6, cameraHMirror);
EEPROM.writeChar(7, currentCachePMFrameRateIndex);
EEPROM.writeChar(8, currentFrameSizeIndex);
EEPROM.writeBool(9, enableOffline);
EEPROM.writeShort(10, customCachePHFrameRate);
EEPROM.writeBool(12, autoSave);
EEPROM.writeChar(13, cameraQuality);
EEPROM.commit();
 
다음 사항에 유의하세요.
  1. Long은 크기가 4바이트이며 int32_t에 사용할 수 있습니다.
  2. Char의 크기는 1바이트이며 int8_t
  3. Bool 도 크기가 1 바이트이며 bool 에 사용할 수 있습니다
  4. Short는 크기가 2바이트이며 int16_t에 사용할 수 있습니다.

그리고 이러한 설정은 ESP32-CAM이 다음과 같이 시작될 때 다시 읽습니다.

int32_t header = EEPROM.readLong(0);
if (header != HEADER) {
 dumbdisplay.logToSerial("EEPROM: not the expected header ... " + String(header) + " vs " + String(HEADER));
 return;
}
cameraBrightness = EEPROM.readChar(4);
cameraVFlip = EEPROM.readBool(5);
cameraHMirror = EEPROM.readBool(6);
currentCachePMFrameRateIndex = EEPROM.readChar(7);
currentFrameSizeIndex = EEPROM.readChar(8);
enableOffline = EEPROM.readBool(9);
customCachePHFrameRate = EEPROM.readShort(10);
autoSave = EEPROM.readBool(12);
cameraQuality = EEPROM.readChar(13);

8 단계 : 스케치 하이라이트 - ESP32 절전 모드

ESP32는 다음과 같이 절전 모드로 전환할 수 있습니다.

esp_sleep_enable_timer_wakeup(sleepTimeoutMillis * 1000); // in micro second
esp_deep_sleep_start();

깨어나면 스케치 / 프로그램은 다음과 같은 변수를 제외하고는 새로 시작한 것과 같습니다.

RTC_DATA_ATTR int32_t tzMins = 0;
RTC_DATA_ATTR int64_t wakeupOfflineSnapMillis = -1;

RTC_DATA_ATTR 접두사가 붙은 변수는 sleep 동안 값을 유지합니다.

9 단계 : 스케치 하이라이트 - 카메라

ESP32-CAM 카메라는 initializeCamera로 초기화됩니다.

bool initializeCamera(framesize_t frameSize, int jpegQuality) {
 esp_camera_deinit(); // just in case, disable camera first
 delay(50);
 camera_config_t config;
 config.ledc_channel = LEDC_CHANNEL_0;
 config.ledc_timer = LEDC_TIMER_0;
 ...
 config.pixel_format = PIXFORMAT_JPEG;
 config.frame_size = frameSize;
 config.jpeg_quality = jpegQuality;
 config.fb_count = 1;
 ...
 esp_err_t camerr = esp_camera_init(&config); // initialize the camera
 if (camerr != ESP_OK) {
  dumbdisplay.logToSerial("ERROR: Camera init failed with error -- " + String(camerr));
 }
 resetCameraImageSettings();
 return (camerr == ESP_OK);
}

'pixel_format'를 'PIXFORMAT_JPEG'로 설정하는 것이 중요합니다. 또한 initializeCamera의 끝에서 resetCameraImageSettings가 호출되어 카메라의 다양한 설정을 지정하며, 이는 UI를 사용하여 카메라 설정을 변경할 때도 호출됩니다

bool resetCameraImageSettings() {
 sensor_t *s = esp_camera_sensor_get();
 if (s == NULL) {
  dumbdisplay.logToSerial("Error: problem reading camera sensor settings");
 return 0;
 }
 ...
 s->set_brightness(s, cameraBrightness); // (-2 to 2) - set brightness
 s->set_quality(s, cameraQuality); // set JPEG quality (0 to 63)
 ...
 return 1;
}

스냅샷 캡처와 관련하여 ESP32-CAM이 절전 모드에서 깨어날 때 "오프라인" 스냅샷을 찍는 방법은 다음과 같습니다

void setup() {
 Serial.begin(115200);
 EEPROM.begin(32);
 initRestoreSettings();
 initializeStorage();
 ...
 if (wakeupOfflineSnapMillis != -1) { // wakeupOfflineSnapMillis will be -1 if ESP is reset
  String localTime;
  long now = getTimeNow(&localTime);
  Serial.println("*** woke up for offline snap ... millis=" + localTime + " ***");
  framesize_t frameSize = cameraFrameSizes[currentFrameSizeIndex];
  if (initializeCamera(frameSize, cameraQuality)) {
   delay(SLEEP_WAKEUP_TAKE_SNAP_DELAY_MS);
   if (true) {
    // it appears that the snap taken will look better if the following "empty" steps are taken
    camera_fb_t* camera_fb = esp_camera_fb_get();
    esp_camera_fb_return(camera_fb);
   }
   camera_fb_t* camera_fb = esp_camera_fb_get();
   saveOfflineSnap(camera_fb->buf, camera_fb->len);
   esp_camera_fb_return(camera_fb);
  } else {
   Serial.println("failed to initialize camera for offline snapping");
  }
  Serial.println("*** going back to sleep ... timeout=" + String(wakeupOfflineSnapMillis /  1000.0) + "s ***");
  Serial.flush();
  esp_sleep_enable_timer_wakeup(wakeupOfflineSnapMillis * 1000); // in micro second
  esp_deep_sleep_start();
  // !!! the above call will not return !!!
 }
 ...
}

통지:

  1. wakeupOfflineSnapMillis의 값은 웨이크업의 정상적인 부팅으로 인해 프로그램이 시작되었는지 여부를 확인하는 데 사용됩니다. "다음 "Offline" 스냅을 위해 절전 모드로 전환되는 시간(밀리초) 값으로 설정됩니다.
  2. 확실히, initializeCamera 가 호출되어 카메라를 초기화합니다.
  3. 카메라를 초기화한 후 카메라를 준비할 수 있는 시간(2초)이 주어집니다
  4. 그런 다음 saveOfflineSnap을 호출하여 저장할 바이트(JPEG 바이트)가 포함된 esp_camera_fb_get를 호출하여 카메라의 버퍼를 검색합니다
  5. 바이트를 저장한 후 buffered는 esp_camera_fb_return를 호출하여 반환됩니다.
  6. 마침내, 그것은 다시 잠이 듭니다

Random Nerd Tutorials ESP32-CAM OV2640 카메라 설정 변경: 밝기, 해상도, 품질, 대비 등 튜토리얼에서 ESP32-CAM에 대한 자세한 내용을 제공합니다.

10 단계 : 스케치 하이라이트 - DumbDisplay

DumbDisplay를 사용하는 다른 모든 사용 사례와 마찬가지로 먼저 전역 DumbDisplay 객체 dumbdisplay를 선언합니다

#if defined(BLUETOOTH)
 #include "esp32dumbdisplay.h"
 DumbDisplay dumbdisplay(new DDBluetoothSerialIO(BLUETOOTH));
#elif defined(WIFI_SSID)
 #include "wifidumbdisplay.h"
 DumbDisplay dumbdisplay(new DDWiFiServerIO(WIFI_SSID, WIFI_PASSWORD),  DD_DEF_SEND_BUFFER_SIZE, 2 * DD_DEF_IDLE_TIMEOUT);
#else
 #include "dumbdisplay.h"
 DumbDisplay dumbdisplay(new DDInputOutput());
#endif
WiFi의 경우 일반적으로 sendBufferSize(두 번째 매개 변수) 및 idleTimeout(세 번째 매개 변수)에 대한 기본값을 사용합니다. 그러나 이 프로그램의 경우 많은 양의 데이터가 DumbDisplay 앱으로 배송될 수 있으므로 idleTimeout이 기본값보다 긴 것이 좋습니다.

그런 다음 여러 전역 도우미 개체 / 포인터가 선언됩니다

DDMasterResetPassiveConnectionHelper pdd(dumbdisplay, true);
GraphicalDDLayer* imageLayer;
JoystickDDLayer* frameSliderLayer;
SelectionDDLayer* frameSizeSelectionLayer;
...
BasicDDTunnel* generalTunnel;
DDMasterResetPassiveConnectionHelper의 생성은 두 개의 매개 변수를 허용합니다
  1. DumbDisplay 객체
  2. "initialize" 람다 표현식을 호출하기 전과 후에 DumbDisplay::recordLayerCommands() / DumbDisplay::p laybackLayerCommands()를 호출할지 여부. DumbDisplay::recordLayerCommands() / DumbDisplay::p laybackLayerCommands()를 호출하면 다양한 레이어의 UI 구성이 더 부드러워집니다.

위의 DumbDisplay 레이어와 "터널"의 수명주기는 DumbDisplay 앱의 연결 및 연결 해제를 모니터링하여 적절한 시간에 적절한 C ++ 람다 표현식을 호출하는 전역 pdd 객체에 의해 관리됩니다. 다음과 같이 loop() 블록에서 "시간 조각"이 협력적으로 제공됩니다.

void loop() {
 pdd.loop([]() {
  initializeDD();
 }, []() {
  if (updateDD(!pdd.firstUpdated())) {
   saveSettings();
  }
 }, []() {
  deinitializeDD();
 });
 if (pdd.isIdle()) {
  handleIdle(pdd.justBecameIdle());
 }
}
  1. pdd.loop()는 3개의 C++ 람다 표현식을 허용합니다 -- []() { ... }
  2. 첫 번째 람다 식은 DumbDisplay 앱이 연결되고 DumbDisplay 구성 요소를 초기화해야 할 때 호출됩니다. 여기서 initializeDD()가 호출됩니다.
  3. 두 번째 람다 식은 DumbDisplay 구성 요소를 업데이트하기 위해 DumbDisplay 앱이 연결되는 동안 호출됩니다. 여기서, updatedDD()를 사용합니다. pdd.firstUpdated()는 이 두 번째 람다 표현식이 DumbDisplay 구성 요소 초기화 후에 이전에 호출되었는지 여부를 알려줍니다(따라서 첫 번째 호출을 의미하지 않음)
  4. 3번째 선택적 람다 식은 DumbDisplay 앱의 연결이 끊어질 때 호출됩니다. 여기서 deinitializeDD()가 호출됩니다.
  5. pdd.loop() 뒤에는 DumbDisplay 앱이 연결되어 있는지 여부에 관계없이 실행될 코드가 있습니다.
  6. pdd.isIdle()은 DumbDisplay가 연결되어 있지 않은지(즉, 유휴 상태인지) 알려줍니다. 유휴 상태에서는 handleIdle() 함수가 호출됩니다. pdd.justBecameIdle()은 DumbDisplay가 방금 연결이 끊어져 유휴 상태인지 알려줍니다.

ESP32-CAM이 처음 연결되면 ESP32-CAM의 시계가 "일반 터널"인 generalTunnel을 통해 휴대폰의 시계와 동기화됩니다

void initializeDD() {
 ...
 // *** please refer to the source in the repository ***
 ...
}
...
bool updateDD(bool isFirstUpdate) {
 ...
 // *** please refer to the source in the repository ***
 ...
}
ESP32-CAM의 시계 동기화 및 설정은 ESP32-CAM이 휴대폰과 연결될 때 한 번만 수행됩니다. 또한 ESP32-CAM의 시계는 절전 중에도 계속 작동합니다.

기본 장면/상태 외에도 UI에는 저장할 스냅샷을 선택하고 "오프라인" 스냅샷을 전송하는 것과 같은 다른 장면이 있습니다. 다른 장면 간에 전환할 때 pinLayers를 호출하여 UI 레이어가 다시 자동 고정됩니다

void pinLayers(State forState) {
if (forState == STREAMING) {
 dumbdisplay.configAutoPin(DDAutoPinConfig('V')
 .beginGroup('H')
 .addLayer(frameSizeSelectionLayer)
 ...
 .addLayer(saveButtonLayer)
 .endGroup()
 .build(), true);
} else if (forState == CONFIRM_SAVE_SNAP) {
 dumbdisplay.configAutoPin(DDAutoPinConfig('V')
 .beginGroup('H')
 .addLayer(imageLayer)
 ...
 .endGroup()
 .build(), true);
} else if (forState == TRANSFER_OFFLINE_SNAPS) {
 dumbdisplay.configAutoPin(DDAutoPinConfig('V')
 .addLayer(imageLayer)
 .addLayer(progressLayer)
 .addLayer(cancelButtonLayer)
 .build(), true);
}
}
configAutoPin의 마지막 매개변수는 autoShowHideLayers입니다. 사실인 경우 자동 고정은 관련되지 않은 다른 모든 레이어를 자동으로 숨깁니다. 이렇게 하면 UI의 다른 장면에 관련되지 않은 모든 레이어를 명시적으로 숨길 필요가 줄어듭니다.

사용자 지정 프레임 속도에 대한 선택 및 프롬프트는 여기에서 강조할 가치가 있습니다. 이 작업은 customFrameRateSelectionLayer 레이어로 수행됩니다

fb = customFrameRateSelectionLayer->getFeedback();
if (fb != NULL) {
 if (fb->type == CUSTOM) {
  int phFrameRate = fb->text.isEmpty() ? customCachePHFrameRate : fb->text.toInt();
  if (phFrameRate >= 1 && phFrameRate <= 3600) {
   customCachePHFrameRate = phFrameRate;
   ...
  } else {
   dumbdisplay.log("invalid custom frame rate!", true);
  }
 } else {
  customFrameRateSelectionLayer->explicitFeedback(0, 0, "'per-hour' frame rate; e.g. " +   String(customCachePHFrameRate) , CUSTOM, "numkeys");
 }
}
  1. 일반적으로 UI의 "피드백"(fb->type) 유형은 CUSTOM이 아닙니다.
  2. 이러한 비 CUSTOM 경우 (즉, "피드백"은 클릭으로 인한 것)에는 위와 같이 customFrameRateSelectionLayer->explicitFeedback 을 호출하십시오.
  3. explicitFeedback은 레이어에 대한 "피드백"을 명시적으로 가짜로 만듭니다.
  4. 그러나 이번에는 "피드백"유형이 CUSTOM (4 번째 매개 변수)입니다.
  5. 처음 3개의 매개변수는 x, y 및 "feedback"의 텍스트에 대한 것입니다.
  6. 마지막 매개 변수 (explicitFeedback numkeys에 대한 옵션)는 "feedback"라우팅 중에 휴대 전화의 가상 키보드로 숫자 값을 입력하라는 메시지가 표시되면 "feedback"의 텍스트를 바꿉니다.
  7. explicitFeedback에 전달된 초기 텍스트는 가상 키보드에서 "힌트"로 사용됩니다
  8. CUSTOM의 경우 fb->text는 입력한 값이 됩니다.

프롬프트의 또 다른 기회는 "오프라인"스냅 샷을 전화기로 전송하기위한 확인입니다. 다음과 같이 generalTunnel 을 통해 수행됩니다.

bool updateDD(bool isFirstUpdate) {
 ...
 // *** please refer to the source in the repository ***
 ...
}
  1. DumbDisplay 앱에서 "동기화 시간"을 얻은 직후 "오프라인" 스냅 전송에 대한 "예/아니오" 확인을 요청해야 하는 경우 "확인" 요청을 처리하기 위해 generalTunnel->reconnectTo 가 호출됩니다.
  2. "예" 또는 "아니요" 응답은 DD_CONNECT_FOR_GET_DATE_TIME와 동일한 방식으로 generalTunnel을 통해 다시 제공됩니다.

"오프라인" 전송 후 전송이 완료되었음을 나타내는 "경고"가 나타납니다. 이것은 dumbdispaly.alert를 호출하여 수행됩니다

 
bool updateDD(bool isFirstUpdate) {
 ...
 // *** please refer to the source in the repository ***
 ...
}

11단계: 빌드 및 업로드

스케치를 조립하고 업로드하고 사용해 보세요!

관심이 있는 경우 직렬 모니터(전송 속도 115200으로 설정)를 사용하여 상황이 언제 발생하는지 관찰할 수 있습니다

Initialize offline snap storage ...
... offlineSnapCount=3 ...
... existing STORAGE ...
$ total: 896 KB
$ used: 180 KB
$ free: 716 KB (79.91%)
... offline snap storage ...
- startOfflineSnapIdx=0
- offlineSnapCount=3
... offline snap storage initialized
bluetooth address: 84:0D:8E:D2:90:EE
*** just became idle ... millis=1067 ***
bluetooth address: 84:0D:8E:D2:90:EE
bluetooth address: 84:0D:8E:D2:90:EE
bluetooth address: 84:0D:8E:D2:90:EE
...
**********
* _SendBufferSize=128
**********
*** just connected ... millis=7133 ***
- got sync time 2024-09-07-10-17-41-+0800
. tz_mins=480
Initializing camera ...
... initialized camera!
...
*** just became idle ... millis=93898(Saturday, September 07 2024 10:19:07+0800) ***
bluetooth address: 84:0D:8E:D2:90:EE
! written offline snap to [/off_000.jpg] ... size=48.39KB
bluetooth address: 84:0D:8E:D2:90:EE
bluetooth address: 84:0D:8E:D2:90:EE
...
*** going to sleep ... millis=153898(Saturday, September 07 2024 10:20:07+0800) ***
...
Initialize offline snap storage ...
... offlineSnapCount=2 ...
... existing STORAGE ...
$ total: 896 KB
$ used: 112 KB
$ free: 784 KB (87.50%)
... offline snap storage ...
- startOfflineSnapIdx=0
- offlineSnapCount=2
... offline snap storage initialized
*** woke up for offline snap ... millis=542(Saturday, September 07 2024 10:20:33+0800) ***
! written offline snap to [/off_002.jpg] ... size=78.91KB
*** going back to sleep ... timeout=27.20s ***
...

 

...
 

프로젝트 결과물

이 프로젝트를 통해 여러분은 프로그래밍과 전자 작업에 대한 실무 경험을 얻을 수 있습니다. 뿐만 아니라, 친구들과 함께 셀카를 찍거나 아름다운 타임랩스 사진을 촬영하며 즐거운 시간을 보낼 수 있습니다.

(가상의 이미지입니다)

위의 가이드를 따라 재미있는 DIY 프로젝트를 시작해 보세요. 여러분의 창의력을 발휘하여 멋진 사진을 촬영하고 저장하는 방법을 배워보세요. 궁금한 점이 있으면 언제든지 댓글로 남겨주세요. Happy snapping! 📷✨

이 블로그 포스트가 여러분의 프로젝트에 도움이 되기를 바랍니다. 많은 즐거움과 학습의 기회가 될 것입니다. Happy coding! 🎉

728x90
반응형