- server.js: add boot_app0 field at 0xe000, raise file limit 8→32 MB - index.html: add 병합/분리 mode toggle, boot_app0 drop zone, numbered split zones - app.js: dynamic mode logic, remove hardcoded flashAddress 0x10000, server now auto-selects 0x0 (merged) or 0x10000 (split) - flash-guide.html: step-by-step Korean flash guide with file table, method A/B walkthrough, flash_args explanation, troubleshooting Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
718 lines
28 KiB
HTML
718 lines
28 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>ESP32 Web Flasher — 플래시 사용 설명서</title>
|
|
<style>
|
|
:root {
|
|
--bg: #0d1117;
|
|
--surf: #161b22;
|
|
--surf2: #21262d;
|
|
--border: #30363d;
|
|
--accent: #00c896;
|
|
--blue: #58a6ff;
|
|
--danger: #f85149;
|
|
--warn: #e3b341;
|
|
--text: #e6edf3;
|
|
--muted: #8b949e;
|
|
}
|
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
body {
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
font-family: 'Segoe UI', 'Apple SD Gothic Neo', sans-serif;
|
|
font-size: 15px;
|
|
line-height: 1.7;
|
|
}
|
|
|
|
/* ── 헤더 ── */
|
|
header {
|
|
background: linear-gradient(135deg, #0e2a23 0%, #111820 100%);
|
|
border-bottom: 1px solid var(--border);
|
|
padding: 32px 24px 28px;
|
|
text-align: center;
|
|
}
|
|
header h1 {
|
|
font-size: 1.9rem;
|
|
font-weight: 700;
|
|
background: linear-gradient(135deg, var(--accent), var(--blue));
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
header p { color: var(--muted); margin-top: 8px; font-size: 14px; }
|
|
|
|
/* ── 레이아웃 ── */
|
|
.wrap { max-width: 860px; margin: 0 auto; padding: 40px 20px 80px; }
|
|
|
|
/* ── 섹션 ── */
|
|
section { margin-bottom: 52px; }
|
|
section h2 {
|
|
font-size: 1.15rem;
|
|
font-weight: 700;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding-bottom: 12px;
|
|
border-bottom: 2px solid var(--border);
|
|
margin-bottom: 22px;
|
|
}
|
|
.sec-num {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 30px; height: 30px;
|
|
border-radius: 8px;
|
|
background: var(--accent);
|
|
color: #000;
|
|
font-size: 14px;
|
|
font-weight: 800;
|
|
flex-shrink: 0;
|
|
}
|
|
h3 { font-size: .95rem; font-weight: 600; color: var(--accent); margin: 20px 0 8px; }
|
|
|
|
/* ── 파일 테이블 ── */
|
|
table { width: 100%; border-collapse: collapse; font-size: 13.5px; margin-bottom: 16px; }
|
|
th { background: var(--surf2); color: var(--muted); font-size: 11px; text-transform: uppercase;
|
|
letter-spacing: .8px; padding: 9px 14px; text-align: left; border: 1px solid var(--border); }
|
|
td { padding: 10px 14px; border: 1px solid var(--border); vertical-align: top; }
|
|
tr:nth-child(even) td { background: rgba(255,255,255,.02); }
|
|
code {
|
|
background: var(--surf2);
|
|
padding: 2px 7px;
|
|
border-radius: 4px;
|
|
font-size: 12px;
|
|
font-family: 'JetBrains Mono', Consolas, monospace;
|
|
color: var(--blue);
|
|
}
|
|
|
|
/* ── 배지 ── */
|
|
.ok { background: rgba(0,200,150,.15); color: var(--accent); padding: 2px 8px; border-radius: 12px; font-size: 11px; font-weight: 700; }
|
|
.skip { background: rgba(139,148,158,.12); color: var(--muted); padding: 2px 8px; border-radius: 12px; font-size: 11px; font-weight: 700; }
|
|
.need { background: rgba(248,81,73,.12); color: var(--danger); padding: 2px 8px; border-radius: 12px; font-size: 11px; font-weight: 700; }
|
|
.opt { background: rgba(227,179,65,.12); color: var(--warn); padding: 2px 8px; border-radius: 12px; font-size: 11px; font-weight: 700; }
|
|
|
|
/* ── 카드 ── */
|
|
.card {
|
|
background: var(--surf);
|
|
border: 1px solid var(--border);
|
|
border-radius: 10px;
|
|
padding: 18px 22px;
|
|
margin-bottom: 14px;
|
|
}
|
|
.card-title { font-size: 12px; font-weight: 700; text-transform: uppercase;
|
|
letter-spacing: .8px; color: var(--muted); margin-bottom: 10px; }
|
|
|
|
/* ── 스텝 박스 ── */
|
|
.step {
|
|
display: flex;
|
|
gap: 16px;
|
|
padding: 16px 0;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.step:last-child { border-bottom: none; }
|
|
.step-n {
|
|
width: 32px; height: 32px;
|
|
border-radius: 50%;
|
|
background: rgba(0,200,150,.15);
|
|
border: 2px solid var(--accent);
|
|
color: var(--accent);
|
|
font-weight: 800;
|
|
font-size: 13px;
|
|
display: flex; align-items: center; justify-content: center;
|
|
flex-shrink: 0;
|
|
margin-top: 2px;
|
|
}
|
|
.step-body .title { font-weight: 600; font-size: 14px; margin-bottom: 5px; }
|
|
.step-body .desc { font-size: 13px; color: var(--muted); }
|
|
|
|
/* ── 경고/팁 박스 ── */
|
|
.alert {
|
|
display: flex;
|
|
gap: 12px;
|
|
padding: 13px 16px;
|
|
border-radius: 8px;
|
|
font-size: 13px;
|
|
margin: 14px 0;
|
|
}
|
|
.alert-warn { background: rgba(227,179,65,.1); border: 1px solid rgba(227,179,65,.3); color: var(--text); }
|
|
.alert-tip { background: rgba(0,200,150,.08); border: 1px solid rgba(0,200,150,.25); color: var(--text); }
|
|
.alert-info { background: rgba(88,166,255,.08); border: 1px solid rgba(88,166,255,.25); color: var(--text); }
|
|
.alert-icon { font-size: 18px; flex-shrink: 0; }
|
|
|
|
/* ── 코드 블록 ── */
|
|
pre {
|
|
background: var(--surf2);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 14px 18px;
|
|
overflow-x: auto;
|
|
font-family: 'JetBrains Mono', Consolas, monospace;
|
|
font-size: 12.5px;
|
|
line-height: 1.65;
|
|
color: #c9d1d9;
|
|
margin: 10px 0 14px;
|
|
}
|
|
pre .c { color: var(--muted); }
|
|
pre .h { color: var(--accent); }
|
|
pre .v { color: var(--warn); }
|
|
|
|
/* ── 방법 탭 스타일 ── */
|
|
.method-tab {
|
|
display: flex;
|
|
gap: 2px;
|
|
margin-bottom: 0;
|
|
border-bottom: 2px solid var(--border);
|
|
}
|
|
.method-lbl {
|
|
padding: 9px 20px;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
cursor: default;
|
|
border-radius: 8px 8px 0 0;
|
|
}
|
|
.method-a { background: rgba(0,200,150,.12); color: var(--accent); border: 1px solid var(--accent); border-bottom: none; }
|
|
.method-b { background: var(--surf2); color: var(--muted); }
|
|
|
|
/* ── 그리드 ── */
|
|
.g2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
|
|
@media(max-width:640px) { .g2 { grid-template-columns: 1fr; } }
|
|
|
|
/* ── 파일 아이콘 행 ── */
|
|
.file-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 10px 14px;
|
|
background: var(--surf2);
|
|
border-radius: 8px;
|
|
margin-bottom: 6px;
|
|
}
|
|
.file-icon { font-size: 18px; }
|
|
.file-name { font-family: Consolas, monospace; font-size: 13px; font-weight: 600; }
|
|
.file-desc { font-size: 12px; color: var(--muted); }
|
|
.file-offset { margin-left: auto; font-family: Consolas, monospace; font-size: 12px; color: var(--warn); }
|
|
|
|
/* ── 스크린샷 박스 ── */
|
|
.mockup {
|
|
background: var(--surf2);
|
|
border: 1px solid var(--border);
|
|
border-radius: 10px;
|
|
padding: 16px;
|
|
font-family: Consolas, monospace;
|
|
font-size: 12px;
|
|
line-height: 2;
|
|
margin: 10px 0;
|
|
}
|
|
.m-ok { color: var(--accent); }
|
|
.m-info { color: var(--muted); }
|
|
.m-warn { color: var(--warn); }
|
|
.m-err { color: var(--danger); }
|
|
|
|
/* ── 푸터 ── */
|
|
footer { text-align: center; color: var(--muted); font-size: 12px; padding: 20px; border-top: 1px solid var(--border); }
|
|
|
|
@media print {
|
|
body { background: #fff; color: #111; }
|
|
header { background: #f0f0f0; }
|
|
.card, pre, .mockup, .file-row { background: #f9f9f9; border-color: #ddd; }
|
|
th { background: #eee; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<header>
|
|
<h1>⚡ ESP32 Web Flasher 사용 설명서</h1>
|
|
<p>Arduino IDE 빌드 파일을 웹 브라우저에서 ESP32에 플래시하는 방법</p>
|
|
</header>
|
|
|
|
<div class="wrap">
|
|
|
|
<!-- ══════════════════════════════════════════════
|
|
1. 빌드 파일 설명
|
|
══════════════════════════════════════════════ -->
|
|
<section id="s1">
|
|
<h2><span class="sec-num">1</span> Arduino IDE 빌드 파일 설명</h2>
|
|
|
|
<p style="color:var(--muted);margin-bottom:16px;">
|
|
<strong style="color:var(--text);">Sketch → Export Compiled Binary</strong> 클릭 후 빌드 폴더
|
|
(<code>build/esp32.esp32.esp32s3/</code> 등)에 생성되는 파일들입니다.
|
|
</p>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>파일명</th>
|
|
<th>크기 (예시)</th>
|
|
<th>플래시 주소</th>
|
|
<th>용도</th>
|
|
<th>사용 여부</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>*.ino.merged.bin</code></td>
|
|
<td>~16 MB</td>
|
|
<td><code>0x0000</code></td>
|
|
<td>전체 병합 바이너리 (모든 파일 합본)</td>
|
|
<td><span class="ok">✓ 권장</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>*.ino.bootloader.bin</code></td>
|
|
<td>~20 KB</td>
|
|
<td><code>0x0000</code></td>
|
|
<td>부트로더</td>
|
|
<td><span class="opt">분리 방식 시</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>*.ino.partitions.bin</code></td>
|
|
<td>~3 KB</td>
|
|
<td><code>0x8000</code></td>
|
|
<td>파티션 테이블</td>
|
|
<td><span class="opt">분리 방식 시</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>boot_app0.bin</code></td>
|
|
<td>~8 KB</td>
|
|
<td><code>0xe000</code></td>
|
|
<td>OTA 부트 선택자</td>
|
|
<td><span class="opt">분리 방식 시</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>*.ino.bin</code></td>
|
|
<td>~1 MB</td>
|
|
<td><code>0x10000</code></td>
|
|
<td>애플리케이션 (실제 코드)</td>
|
|
<td><span class="opt">분리 방식 시</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>*.ino.elf</code></td>
|
|
<td>~15 MB</td>
|
|
<td>—</td>
|
|
<td>디버그 심볼 (GDB용)</td>
|
|
<td><span class="skip">불필요</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>*.ino.map</code></td>
|
|
<td>~17 MB</td>
|
|
<td>—</td>
|
|
<td>메모리 맵 (분석용)</td>
|
|
<td><span class="skip">불필요</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>flash_args</code></td>
|
|
<td>~200 B</td>
|
|
<td>—</td>
|
|
<td>esptool 플래시 인수 (참고용)</td>
|
|
<td><span class="skip">참고용</span></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<div class="alert alert-tip">
|
|
<span class="alert-icon">💡</span>
|
|
<div>
|
|
<strong>flash_args 파일을 열면</strong> 어떤 파일이 어느 주소에 들어가는지 정확히 기록되어 있습니다.
|
|
<pre style="margin:8px 0 0;font-size:12px;">--flash-mode dio --flash-freq 80m --flash-size 16MB
|
|
0x0 bootloader.bin
|
|
0x8000 partitions.bin
|
|
0xe000 boot_app0.bin
|
|
0x10000 app.bin</pre>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ══════════════════════════════════════════════
|
|
2. 업로드 방식 선택
|
|
══════════════════════════════════════════════ -->
|
|
<section id="s2">
|
|
<h2><span class="sec-num">2</span> 업로드 방식 선택</h2>
|
|
|
|
<div class="g2">
|
|
<div class="card" style="border-color:var(--accent);">
|
|
<div class="card-title" style="color:var(--accent);">방법 A — 병합 바이너리 (권장)</div>
|
|
<p style="font-size:13px;color:var(--muted);margin-bottom:12px;">
|
|
모든 파티션이 하나로 합쳐진 <code>*.merged.bin</code> 파일 하나만 업로드합니다.
|
|
가장 간단하고 오류 가능성이 낮습니다.
|
|
</p>
|
|
<div style="font-size:13px;">
|
|
<div style="color:var(--accent);">✓ 장점</div>
|
|
<ul style="padding-left:18px;color:var(--muted);line-height:2;">
|
|
<li>파일 하나로 완전한 플래시</li>
|
|
<li>주소 설정 불필요</li>
|
|
<li>파일 누락 실수 없음</li>
|
|
</ul>
|
|
</div>
|
|
<div style="font-size:13px;margin-top:8px;">
|
|
<div style="color:var(--warn);">⚠ 주의</div>
|
|
<ul style="padding-left:18px;color:var(--muted);line-height:2;">
|
|
<li>파일 크기가 클 수 있음 (Flash 크기 = 파일 크기)</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-title">방법 B — 분리 파일</div>
|
|
<p style="font-size:13px;color:var(--muted);margin-bottom:12px;">
|
|
bootloader, partitions, boot_app0, app을 각각 업로드합니다.
|
|
파일 크기가 작고 부분 업데이트가 가능합니다.
|
|
</p>
|
|
<div style="font-size:13px;">
|
|
<div style="color:var(--accent);">✓ 장점</div>
|
|
<ul style="padding-left:18px;color:var(--muted);line-height:2;">
|
|
<li>앱만 따로 업데이트 가능</li>
|
|
<li>파일 크기 작음 (앱만 ~1 MB)</li>
|
|
</ul>
|
|
</div>
|
|
<div style="font-size:13px;margin-top:8px;">
|
|
<div style="color:var(--warn);">⚠ 주의</div>
|
|
<ul style="padding-left:18px;color:var(--muted);line-height:2;">
|
|
<li>4개 파일을 정확히 맞춰야 함</li>
|
|
<li>파일 누락 시 부팅 실패</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ══════════════════════════════════════════════
|
|
3. 방법 A 단계별 가이드
|
|
══════════════════════════════════════════════ -->
|
|
<section id="s3">
|
|
<h2><span class="sec-num">3</span> 방법 A — 병합 바이너리 플래시 (권장)</h2>
|
|
|
|
<div class="method-tab">
|
|
<span class="method-lbl method-a">병합 바이너리 모드</span>
|
|
</div>
|
|
<div class="card" style="border-radius:0 8px 8px 8px;border-top:none;">
|
|
|
|
<div class="step">
|
|
<div class="step-n">1</div>
|
|
<div class="step-body">
|
|
<div class="title">브라우저 준비</div>
|
|
<div class="desc">
|
|
<strong>Chrome 89+</strong> 또는 <strong>Edge 89+</strong>를 사용하세요.
|
|
<br>Web Flasher 주소: <code>http://localhost:3100</code> (Docker와 같은 PC인 경우)
|
|
<br><span style="color:var(--danger);">Firefox / Safari는 Web Serial API 미지원으로 플래시 불가</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="step">
|
|
<div class="step-n">2</div>
|
|
<div class="step-body">
|
|
<div class="title">ESP32 보드 USB 연결</div>
|
|
<div class="desc">
|
|
ESP32-S3는 <strong>내장 USB 포트</strong>를 사용하면 별도 드라이버가 필요 없습니다.
|
|
<br>USB-UART 변환기(CP2102, CH340)를 사용하는 경우 칩 드라이버를 미리 설치하세요.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="step">
|
|
<div class="step-n">3</div>
|
|
<div class="step-body">
|
|
<div class="title">시리얼 연결 확인 (STEP 1)</div>
|
|
<div class="desc">
|
|
웹 플래셔의 <strong>포트 연결</strong> 버튼을 클릭하고 브라우저 포트 선택창에서 ESP32 포트를 선택합니다.
|
|
<br>아래와 같이 Espressif 확인 메시지가 나오면 정상입니다.
|
|
<div class="mockup">
|
|
<span class="m-ok">✓ 포트 연결 성공</span><br>
|
|
<span class="m-info"> VID: 0x303A PID: 0x1001</span><br>
|
|
<span class="m-ok"> Espressif 장치 감지됨 (ESP32S3 가능성 높음)</span><br>
|
|
<span class="m-info"> 포트를 닫았습니다 (플래시 시 자동 재연결)</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="step">
|
|
<div class="step-n">4</div>
|
|
<div class="step-body">
|
|
<div class="title">업로드 방식 선택 — "병합 바이너리" 선택</div>
|
|
<div class="desc">
|
|
<strong>펌웨어 업로드</strong> 탭에서 <strong style="color:var(--accent);">병합 바이너리</strong> 버튼이 선택되어 있는지 확인합니다.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="step">
|
|
<div class="step-n">5</div>
|
|
<div class="step-body">
|
|
<div class="title">merged.bin 파일 선택</div>
|
|
<div class="desc">
|
|
빌드 폴더에서 <code>CANFD_Logger.ino.merged.bin</code> (또는 프로젝트명.ino.merged.bin)을 드래그하거나 클릭하여 선택합니다.
|
|
<div class="file-row" style="margin-top:10px;">
|
|
<span class="file-icon">📦</span>
|
|
<div>
|
|
<div class="file-name">CANFD_Logger.ino.merged.bin</div>
|
|
<div class="file-desc">전체 병합 바이너리 — 이 파일 하나만 필요</div>
|
|
</div>
|
|
<div class="file-offset">offset 0x0</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="step">
|
|
<div class="step-n">6</div>
|
|
<div class="step-body">
|
|
<div class="title">메타데이터 입력 후 업로드</div>
|
|
<div class="desc">
|
|
이름, 버전, 칩 패밀리(ESP32-S3)를 입력하고 <strong>서버에 업로드</strong>를 클릭합니다.
|
|
<br>업로드 완료 후 자동으로 <strong>플래시</strong> 탭으로 이동합니다.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="step">
|
|
<div class="step-n">7</div>
|
|
<div class="step-body">
|
|
<div class="title">플래시 실행</div>
|
|
<div class="desc">
|
|
<strong>플래시</strong> 탭에서 방금 업로드한 펌웨어를 클릭하여 선택한 뒤,
|
|
<strong>⚡ 플래시 실행</strong> 버튼을 누릅니다.
|
|
<br>esp-web-tools 창에서 포트를 선택하면 자동으로 플래싱이 진행됩니다.
|
|
<div class="alert alert-warn" style="margin-top:10px;">
|
|
<span class="alert-icon">⚠</span>
|
|
<div>플래시 중 USB 케이블 분리 금지. 일부 보드는 BOOT 버튼을 누른 채로 연결해야 합니다.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ══════════════════════════════════════════════
|
|
4. 방법 B 단계별 가이드
|
|
══════════════════════════════════════════════ -->
|
|
<section id="s4">
|
|
<h2><span class="sec-num">4</span> 방법 B — 분리 파일 플래시</h2>
|
|
|
|
<div class="method-tab">
|
|
<span class="method-lbl method-b">분리 파일 모드</span>
|
|
</div>
|
|
<div class="card" style="border-radius:0 8px 8px 8px;border-top:none;">
|
|
|
|
<p style="font-size:13px;color:var(--muted);margin-bottom:16px;">
|
|
단계 1~3(브라우저 준비 → USB 연결 → 시리얼 연결 확인)은 방법 A와 동일합니다.
|
|
<strong style="color:var(--text);">4단계부터</strong> 차이가 있습니다.
|
|
</p>
|
|
|
|
<div class="step">
|
|
<div class="step-n">4</div>
|
|
<div class="step-body">
|
|
<div class="title">업로드 방식 — "분리 파일" 선택</div>
|
|
<div class="desc">
|
|
업로드 탭 상단에서 <strong>분리 파일</strong>을 선택합니다.
|
|
bootloader, partitions, boot_app0, app 4개 드롭존이 표시됩니다.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="step">
|
|
<div class="step-n">5</div>
|
|
<div class="step-body">
|
|
<div class="title">파일 4개 각각 선택</div>
|
|
<div class="desc">
|
|
빌드 폴더에서 아래 파일들을 각 드롭존에 넣습니다.
|
|
|
|
<div class="file-row" style="margin-top:10px;">
|
|
<span class="file-icon">🔧</span>
|
|
<div>
|
|
<div class="file-name">CANFD_Logger.ino.bootloader.bin</div>
|
|
<div class="file-desc">① 부트로더 드롭존에 넣기</div>
|
|
</div>
|
|
<div class="file-offset">0x0000</div>
|
|
</div>
|
|
<div class="file-row">
|
|
<span class="file-icon">📋</span>
|
|
<div>
|
|
<div class="file-name">CANFD_Logger.ino.partitions.bin</div>
|
|
<div class="file-desc">② 파티션 테이블 드롭존에 넣기</div>
|
|
</div>
|
|
<div class="file-offset">0x8000</div>
|
|
</div>
|
|
<div class="file-row">
|
|
<span class="file-icon">🔁</span>
|
|
<div>
|
|
<div class="file-name">boot_app0.bin</div>
|
|
<div class="file-desc">③ Boot App0 드롭존에 넣기 (OTA 미사용 시 생략 가능)</div>
|
|
</div>
|
|
<div class="file-offset">0xe000</div>
|
|
</div>
|
|
<div class="file-row">
|
|
<span class="file-icon">📦</span>
|
|
<div>
|
|
<div class="file-name">CANFD_Logger.ino.bin</div>
|
|
<div class="file-desc">④ 애플리케이션 드롭존에 넣기</div>
|
|
</div>
|
|
<div class="file-offset">0x10000</div>
|
|
</div>
|
|
|
|
<div class="alert alert-warn" style="margin-top:12px;">
|
|
<span class="alert-icon">⚠</span>
|
|
<div>
|
|
<code>boot_app0.bin</code>은 빌드 폴더가 아닌 Arduino ESP32 코어 폴더에 있을 수 있습니다.<br>
|
|
<code>%LOCALAPPDATA%\Arduino15\packages\esp32\hardware\esp32\버전\tools\partitions\boot_app0.bin</code>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="step">
|
|
<div class="step-n">6</div>
|
|
<div class="step-body">
|
|
<div class="title">메타데이터 입력 후 업로드 → 플래시</div>
|
|
<div class="desc">
|
|
방법 A의 6~7단계와 동일하게 진행합니다.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ══════════════════════════════════════════════
|
|
5. 트러블슈팅
|
|
══════════════════════════════════════════════ -->
|
|
<section id="s5">
|
|
<h2><span class="sec-num">5</span> 자주 발생하는 문제</h2>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr><th>증상</th><th>원인</th><th>해결 방법</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>포트 연결 버튼이 비활성화</td>
|
|
<td>Firefox/Safari 사용</td>
|
|
<td>Chrome 또는 Edge 89+ 사용</td>
|
|
</tr>
|
|
<tr>
|
|
<td>"지원되지 않는 브라우저" 배너</td>
|
|
<td>HTTP + 외부 IP 접속</td>
|
|
<td>Docker와 같은 PC에서 <code>localhost:3100</code>으로 접속</td>
|
|
</tr>
|
|
<tr>
|
|
<td>포트 선택창에 ESP32가 안 보임</td>
|
|
<td>드라이버 미설치 또는 케이블 불량</td>
|
|
<td>데이터 전송 가능한 USB 케이블 사용, CP2102/CH340 드라이버 설치</td>
|
|
</tr>
|
|
<tr>
|
|
<td>플래시 후 부팅 안 됨</td>
|
|
<td>Flash 크기 불일치</td>
|
|
<td>Arduino IDE 보드 설정의 Flash Size가 실제 보드와 일치하는지 확인 후 재컴파일</td>
|
|
</tr>
|
|
<tr>
|
|
<td>업로드 실패 (파일 크기 초과)</td>
|
|
<td>merged.bin이 32 MB 초과</td>
|
|
<td>분리 파일 방식 사용 (앱만 ~1 MB)</td>
|
|
</tr>
|
|
<tr>
|
|
<td>플래시 중 연결 끊김</td>
|
|
<td>USB 전원 불안정</td>
|
|
<td>허브 대신 PC 직접 연결, 전원 공급이 충분한 포트 사용</td>
|
|
</tr>
|
|
<tr>
|
|
<td>BOOT 버튼 관련 오류</td>
|
|
<td>ROM 부트로더 진입 실패</td>
|
|
<td>BOOT 버튼을 누른 상태로 ESP32 연결 또는 RST 후 BOOT 누르기</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</section>
|
|
|
|
<!-- ══════════════════════════════════════════════
|
|
6. Flash 플래그 설명
|
|
══════════════════════════════════════════════ -->
|
|
<section id="s6">
|
|
<h2><span class="sec-num">6</span> flash_args 플래그 설명</h2>
|
|
|
|
<p style="color:var(--muted);margin-bottom:14px;">
|
|
<code>flash_args</code> 파일에 기록된 플래그는 Arduino IDE가 컴파일 시 설정한 값입니다.
|
|
웹 플래셔는 이 값을 바이너리에서 자동으로 읽으므로 별도 입력이 필요 없습니다.
|
|
</p>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr><th>플래그</th><th>값 (예시)</th><th>의미</th><th>웹 플래셔 입력 필요?</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>--flash-mode</code></td>
|
|
<td>dio</td>
|
|
<td>SPI 플래시 통신 방식 (DIO = Dual I/O)</td>
|
|
<td><span class="skip">불필요 (바이너리 내장)</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>--flash-freq</code></td>
|
|
<td>80m</td>
|
|
<td>플래시 클럭 주파수 (80 MHz)</td>
|
|
<td><span class="skip">불필요 (바이너리 내장)</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>--flash-size</code></td>
|
|
<td>16MB</td>
|
|
<td>플래시 메모리 용량 (실제 보드와 일치해야 함)</td>
|
|
<td><span class="skip">불필요 (바이너리 내장)</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td>PSRAM 설정</td>
|
|
<td>컴파일 옵션</td>
|
|
<td>PSRAM 활성화 여부 (ESP32-S3 4MB/8MB PSRAM)</td>
|
|
<td><span class="skip">불필요 (바이너리 내장)</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td>CPU 주파수</td>
|
|
<td>240 MHz</td>
|
|
<td>프로세서 동작 클럭</td>
|
|
<td><span class="skip">불필요 (바이너리 내장)</span></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<div class="alert alert-tip">
|
|
<span class="alert-icon">💡</span>
|
|
<div>
|
|
Arduino IDE에서 PSRAM, CPU 주파수, Flash 크기 등을 설정하면 모두 <strong>.bin 파일 안에 컴파일되어 저장</strong>됩니다.
|
|
웹 플래셔는 이 .bin 파일을 그대로 ESP32에 써넣기만 하므로, 이 설정들을 다시 입력할 필요가 없습니다.
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ══════════════════════════════════════════════
|
|
7. 브라우저 호환성
|
|
══════════════════════════════════════════════ -->
|
|
<section id="s7">
|
|
<h2><span class="sec-num">7</span> 브라우저 및 환경 호환성</h2>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr><th>환경</th><th>지원</th><th>비고</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr><td>Chrome 89+</td><td><span class="ok">✓ 지원</span></td><td>권장</td></tr>
|
|
<tr><td>Edge 89+</td><td><span class="ok">✓ 지원</span></td><td>권장</td></tr>
|
|
<tr><td>Firefox</td><td><span class="need">✗ 미지원</span></td><td>Web Serial API 없음</td></tr>
|
|
<tr><td>Safari</td><td><span class="need">✗ 미지원</span></td><td>Web Serial API 없음</td></tr>
|
|
<tr><td>http://localhost</td><td><span class="ok">✓ 동작</span></td><td>보안 컨텍스트 예외</td></tr>
|
|
<tr><td>http://192.168.x.x (IP)</td><td><span class="need">✗ 미동작</span></td><td>HTTPS 필요</td></tr>
|
|
<tr><td>https://도메인</td><td><span class="ok">✓ 동작</span></td><td>SSL 인증서 필요</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</section>
|
|
|
|
</div>
|
|
|
|
<footer>
|
|
ESP32 Web Flasher 사용 설명서 — 2025년 5월
|
|
</footer>
|
|
|
|
</body>
|
|
</html>
|