단계 3 — 스마트폰으로 첫 주행 (Python 없이)

차시 2차시 · 선수 학습 단계 1, 2 (배선 완료, HC-SR04 추가 권장)

이번 단계 목표 ESP32-CAM을 웹서버로 만들어, 스마트폰 브라우저로 차량을 운전하고 간단한 자율주행(회피·순찰·따라가기)까지 체험한다.

1. 왜 이 단계가 있는가?

배선이 끝났지만 아직 Python을 배우지 않은 상태입니다. 차량이 실제로 움직이는지 빨리 확인하면 다음 두 가지 효과가 있습니다.

  1. 배선이 잘됐는지 점검 — 안 움직이면 회로 문제, 잘 움직이면 자신감 ↑
  2. 동기 부여 — 자기가 만든 차가 움직이는 첫 경험은 학습 의욕을 크게 끌어올립니다
이번 단계는 공유기·노트북·인터넷이 필요 없습니다. ESP32-CAM이 직접 Wi-Fi(Auto_Car_01)를 만들고, 스마트폰만 그 Wi-Fi에 접속하면 됩니다.

2. 학습 목표

3. 이론

3-1. AP 모드 vs STA 모드

ESP32-CAM의 Wi-Fi는 두 가지 역할을 할 수 있습니다.

모드의미본 프로젝트에서
AP (Access Point)자기가 직접 Wi-Fi를 만들어 다른 기기를 받음단계 3 (이번)
STA (Station)다른 Wi-Fi에 손님으로 접속단계 4 이후
        Auto_Car_01 (ESP32가 만든 Wi-Fi)
              ↓
   ┌──────────┴──────────┐
   │                     │
ESP32-CAM            스마트폰
(192.168.4.1)     (192.168.4.x)
💡 폰을 ESP32 Wi-Fi에 접속하면 폰은 인터넷이 끊깁니다(정상). 차량 조종이 끝나면 원래 Wi-Fi로 다시 연결하세요.

3-2. 웹서버란?

"주소를 입력하면 페이지를 보내주는 프로그램을 어떤 기기가 항상 켜두고 있는 것"
스마트폰 ──[ http://192.168.4.1/drive ]──→ ESP32-CAM
         ←──── HTML 페이지 ─────────────
스마트폰 ──[ http://192.168.4.1/move?speed=100&steering=90 ]──→ ESP32
         ←──── {"speed":100,"steering":90} ─────────────
ESP32 AP 모드의 IP는 항상 192.168.4.1로 고정. 외워두면 편합니다.

3-3. HTTP GET과 쿼리 문자열

HTTP GET: "페이지를 가져다 줘" 요청
쿼리 문자열: 주소 뒤에 ?key=value&key2=value2 형태로 추가 정보 전달

URL포트의미
/ 또는 /drive80조종 페이지(HTML) 받기
/move?speed=100&steering=9080속도 100, 조향 90으로 명령
/stop80즉시 정지 + 수동 모드로 복귀
/mode?m=manual|avoid|patrol|follow80자율주행 모드 전환
/distance80마지막 측정 거리(cm). 999면 측정 불가
/stream81MJPEG 영상 스트림 (별도 포트)
💡 포트 80(제어)과 포트 81(스트림)을 분리한 이유: 스트림은 한 번 시작하면 연결을 계속 점유합니다. 포트를 분리해야 영상이 흐르는 동안에도 /move, /distance 같은 짧은 명령이 끊기지 않습니다.

3-4. 펌웨어 안에 HTML이 들어있다고?

esp32_cam_phone_drive.inoDRIVE_HTML[]를 펼쳐 보면 페이지 전체가 C 문자열로 들어있습니다.

펌웨어 (.ino)
   │
   ├─ HTML 한 덩어리 ───→ 브라우저에 그대로 송신
   ├─ JavaScript 한 덩어리 (페이지에 포함)
   └─ 모터 제어 함수

4. 안전 기능 (펌웨어가 자동으로 함)

안전장치동작
속도 상한최대 ±200 (펌웨어에서 강제)
조향 범위 제한45° ~ 135° (벗어난 값은 잘라냄)
통신 끊김 자동 정지0.8초간 명령이 없으면 자동으로 정지 (워치독)
/stop 즉시 모드 복귀자율주행 중에도 /stop 호출 시 수동 모드로 강제 전환
회피 K-턴 시퀀스자율 모드에서 장애물 발견 시 정지 → 후진 → 정지 → 전진 단계로 안전 회피
⚠️ 그래도 항상 사람이 정지 버튼 위에 손가락을 두는 것이 우선입니다.

5. 실습 절차

5-0. 초음파 거리 센서(HC-SR04) 추가 배선 — 권장

자율주행 모드(회피·순찰·따라가기)와 화면의 거리 뱃지(📏 cm)는 HC-SR04가 있어야 동작합니다. 단계 2의 기본 배선에 다음 4선을 추가하세요.

HC-SR04ESP32-CAM 핀보드 표기비고
VCC3.3V3V35V가 모터·서보에 점유돼도 깔끔. 일부 모듈은 5V가 더 안정적
GNDIO0 옆 GNDGND⚠️ ESP32-CAM은 GND 핀이 3개 있는데 IO0 바로 옆 GND를 권장. 다른 GND에 꽂으면 부팅 루프 발생 사례 있음
TRIGGPIO 4IO4보드의 흰색 플래시 LED와 공유. 측정 펄스가 10µs로 매우 짧아 점등은 보이지 않음
ECHOGPIO 2IO2부팅 strapping 핀이지만 입력 사용은 부팅 후라 무관

안테나 쪽 핀헤더 — 위에서 아래로

[ESP32-CAM 안테나 쪽 핀헤더]
   3V3        ← HC-SR04 VCC
   IO16       ← (사용 안 함)
   IO0        ← (다운로드 모드용 / 사용 안 함)
   GND        ← HC-SR04 GND  ★ 이 GND를 사용 (IO0 옆) ★
   5V         ← (배터리 +)
   IO13       ← 모터 IN1
   IO12       ← 모터 PWM (ENA)
   IO14       ← 서보 신호선
   IO15       ← 모터 IN2
   IO2        ← HC-SR04 ECHO   ⬇ 바로 옆!
   IO4        ← HC-SR04 TRIG   ⬆
💡 HC-SR04를 달지 않아도 수동 조종은 정상 동작합니다. 거리 뱃지는 📏 ∞로 표시됩니다.

5-1. 펌웨어 업로드

Arduino IDE 설정 (처음 한 번만)

  1. 보드 추가: 파일 → 환경설정 → 추가 보드 매니저 URL
    https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
  2. 보드 매니저: "esp32" 검색 → 설치
  3. 라이브러리 설치: 스케치 → 라이브러리 포함 → 라이브러리 관리ESP32Servo 검색 → 설치

AP 정보 (이미 펌웨어에 설정됨)

const char *ap_ssid     = "Auto_Car_01";
const char *ap_password = "";   // 개방형 (비밀번호 없음)
📡 개방형 네트워크입니다. 폰의 Wi-Fi 목록에서 Auto_Car_01을 누르면 비밀번호 입력 없이 바로 접속됩니다.
같은 교실에서 여러 팀이 동시에 사용할 때는 팀별로 SSID를 다르게 하세요: "Auto_Car_07", "Auto_Car_12" 등.

보드 설정

업로드

  1. GPIO 0 → GND 점프(다운로드 모드)
  2. RESET 버튼 한 번 누름
  3. 업로드(→) 버튼
  4. 끝나면 GPIO 0 점프 해제 후 다시 RESET

5-2. AP 동작 확인

  1. 시리얼 모니터 열기 (115200 baud)
  2. RESET 버튼 누름
  3. 다음과 같은 출력 확인:
    AP 활성 — SSID: Auto_Car_01
           보안: 개방형 (비밀번호 없음)
           채널: 1
           AP IP: 192.168.4.1
    
    [스마트폰 사용 절차]
      1) 폰 Wi-Fi 설정 → "Auto_Car_01" 선택
      2) 비밀번호 없음 — 바로 연결됨 (개방형)
      3) 브라우저로 http://192.168.4.1/  접속

5-3. 스마트폰 연결

  1. 스마트폰의 설정 → Wi-Fi
  2. 목록에서 Auto_Car_01 선택 — 비밀번호 입력 화면 없이 바로 연결됨
  3. ⚠️ "이 네트워크는 인터넷에 연결되지 않을 수 있습니다" 경고가 뜨면 → Android: "그대로 연결" / iOS: 그대로 두면 연결 유지됨
  4. 브라우저(Chrome/Safari) 주소창에 입력: http://192.168.4.1
  5. 다음 화면이 뜨면 성공:
    ┌────────────────────────────────────┐
    │  [ 영상 스트림 (포트 81) ]         │
    │  ●연결됨   📏 32 cm   3Hz          │
    ├────────────────────────────────────┤
    │ 최대 속도 ─────●───── 100          │
    │ 조향 폭   ──●──────── 30           │
    │ 조향 중심 [−] 90 [+]  [↺ 90]       │
    │ [🔘 버튼 조향] [🎯 기울기 조향]    │
    │ [🛑수동][🚧회피][👮순찰][🐕따라]   │
    │              ▲                     │
    │           ◀ 정지 ▶                │
    │              ▼                     │
    └────────────────────────────────────┘

5-4. 첫 주행 (⚠️ 안전 수칙)

반드시 다음 순서로:

6. 활동

활동 2.5-A. 페이지 접속 ⭐ Easy

브라우저 주소창에 IP를 입력해 페이지를 띄우고, 다음을 캡처해 제출:

활동 2.5-B. 안전 점검 후 실주행 ⭐⭐ Standard

바퀴 공중 띄움 상태에서 다음을 확인:

미션 — 바닥에 내려놓고 직선 1m → 좌회전 → 직선 1m → 정지 코스를 완주.

활동 2.5-C. URL 직접 조종 ⭐⭐⭐ Challenge

브라우저 주소창에 직접 입력해도 차량이 반응하는지 확인:

http://192.168.4.1/move?speed=80&steering=110
http://192.168.4.1/stop
http://192.168.4.1/distance
http://192.168.4.1/mode?m=avoid
http://192.168.4.1/mode?m=manual

도전 질문

활동 2.5-D. 조향 중심 트림 ⭐⭐ Standard

조작동작
짧게 탭-1° (좌측으로 중심 이동)
+ 짧게 탭+1° (우측으로 중심 이동)
버튼 길게 누름0.4초 후 자동 반복 (100ms 간격)
↺ 90기본값(90°) 복귀

값은 localStorage에 저장 — 페이지 새로고침 후에도 유지됩니다. 범위: 70° ~ 110°로 자동 클램프.

활동 2.5-E. 기울기 센서 조향 ⭐⭐⭐ Challenge

  1. 🎯 기울기 조향 버튼 탭 → iOS는 권한 팝업 → "허용"
  2. 화면 아래에 γ-12° β3° ◀ 서보78° 같은 진단 라인이 뜨면 정상
  3. 폰을 자연스럽게 좌·우로 lean → 서보가 따라옴
Android 크롬에서 "신호 없음"이 뜨면: chrome://flags → "Insecure origins treated as secure" → Enabled → http://192.168.4.1 추가 → Relaunch

활동 2.5-F. 자율주행 4모드 ⭐⭐⭐ Challenge — HC-SR04 필요

모드동작
🛑 수동사용자가 D-pad·기울기로 모든 조작 (기본)
🚧 회피직진하다가 18cm 이하 장애물 → 정지 → 후진(서보 우) → 정지 → 전진(서보 좌)으로 우회. Ackermann K-턴
👮 순찰회피와 동일하지만 회전 방향이 좌·우 교대 (지그재그 순찰)
🐕 따라가기18~35cm를 적정 거리로 보고, 가까우면 후진, 멀면 전진. 120cm 이상은 "대상 놓침" → 정지
⚠️ 자율 모드는 사람이 차 옆에서 정지 버튼에 손을 대고 시연합니다. 정지 버튼은 모드와 무관하게 항상 즉시 수동 모드로 복귀됩니다.

7. 작동 안 할 때

증상점검
Wi-Fi 목록에 Auto_Car_01 없음ESP32 전원 확인, 시리얼에 "AP 활성" 메시지 떴는지 확인, 폰 Wi-Fi 새로고침
비밀번호 입력 화면이 뜸펌웨어의 ap_password가 8자 이상으로 들어감 → 빈 문자열 ""로 변경 후 재업로드
접속은 되는데 페이지 안 뜸URL이 정확히 http://192.168.4.1/인지 (https 아님!)
페이지는 뜨는데 영상 XESP32 발열·전원 부족 (5V 2A 권장), 전원선 다시 꽂아 보기
슬라이더 움직여도 차 반응 없음시리얼 모니터에서 명령 수신 로그 확인, 모터 결선 점검
거리 뱃지가 항상 📏 ∞HC-SR04 미설치 / VCC·GND 배선 점검 / TRIG=GPIO4, ECHO=GPIO2 확인
HC-SR04 연결 시 부팅 무한 루프GND 핀 위치 변경 — IO0 옆 GND를 사용
거리값이 들쭉날쭉평평한 벽으로 테스트. VCC를 3.3V→5V로 바꾸면 안정될 때도 있음
직진이 한쪽으로 쏠림활동 2.5-D의 조향 중심 트림으로 보정. localStorage에 저장되므로 한 번만
기울기 모드에서 "신호 없음"활동 2.5-E의 chrome flag 가이드 참고 (Android) / 설정 → Safari → 모션 액세스 ON (iOS)

자세한 트러블슈팅은 문제해결.md 참고.

8. 핵심 개념 정리

개념설명
웹서버주소 입력 시 페이지를 보내주는 프로그램. ESP32-CAM 자체가 두 개(80, 81)를 동시에 돌린다
HTTP GET"페이지를 가져다 줘" 요청 방식
쿼리 문자열URL 끝의 ?key=value — 추가 정보 전달
MJPEG프레임을 JPEG로 압축해 연속 송신
워치독(Watchdog)통신이 끊기면 자동 안전 동작
AP(접속점)Wi-Fi 신호를 만들어주는 장치 (공유기/핫스팟)
HC-SR04초음파 거리 센서. 음파 왕복 시간으로 cm 환산 (0.0343/2)
Ackermann K-턴앞바퀴 조향 차량이 좁은 공간에서 후진·전진을 결합한 방향 전환 동작
localStorage브라우저 안의 영구 저장소. 새로고침해도 값이 유지됨

9. 두 펌웨어의 차이

항목esp32_cam_phone_drive.ino (이번 단계)esp32_cam.ino (단계 4 이후)
Wi-Fi 역할AP (자체 송출)STA (다른 Wi-Fi에 접속)
조종 방법스마트폰 웹페이지Python UDP
HTML UI✅ 펌웨어 안에 내장❌ 없음
UDP 수신❌ 없음✅ 포트 4210
안전 워치독✅ 0.8초❌ Python에 의존
자율주행✅ 회피·순찰·따라가기 (펌웨어 내장)❌ Python이 담당 (단계 8~9)
사용 시점단계 3만단계 4~9
단계 4 진입 전, esp32_cam.ino를 다시 업로드하세요.

단계 4 이후 네트워크 구성 (예고)

                노트북이 만든 핫스팟 "Auto_Car_01"
                          ↓
          ┌───────────────┴───────────────┐
          │                               │
   ESP32-CAM (STA)                노트북 자기 자신
   (DHCP IP, 예: 192.168.137.74)  (192.168.137.1)
                                          │
                                  Python 차선 인식
                                  → UDP 명령 송신

노트북 핫스팟 켜는 방법 (Windows 11)

  1. 설정 → 네트워크 및 인터넷 → 모바일 핫스팟
  2. 속성 → 편집: 네트워크 이름 Auto_Car_01, 네트워크 대역 2.4 GHz (ESP32는 5GHz 미지원)
  3. 모바일 핫스팟 켜기
esp32_cam.ino에는 const char *ssid = "Auto_Car_01";이 기본값으로 설정되어 있습니다. 노트북 핫스팟에 비밀번호가 있으면 password 자리에 같은 값을 넣고 재업로드하세요.

10. 자기평가 (5점 척도)

항목12345
웹서버·HTTP의 의미를 한 줄로 설명할 수 있다
펌웨어를 업로드하고 IP를 확인할 수 있다
스마트폰으로 차량을 안전하게 조종했다
첫 주행 안전 수칙을 모두 지켰다
URL 직접 입력으로도 차량을 조작해 보았다
조향 중심 트림으로 직진을 보정해 보았다
자율 모드(회피/순찰/따라가기) 중 하나를 시연했다

11. 동료 평가 (T 역량 활동)

팀원이 운전할 때 안전을 도와준 경험을 적어 봅시다.

예) "○○ 친구가 처음 운전할 때 정지 버튼 위치를 알려줬다."