feat: support merged.bin, boot_app0, upload mode toggle + flash guide

- 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>
This commit is contained in:
root
2026-05-19 05:26:53 +09:00
parent cb9df54abb
commit 3a662affb6
4 changed files with 842 additions and 47 deletions

View File

@@ -190,29 +190,71 @@ function setupDropZone(zoneId, inputId) {
setupDropZone('drop-firmware', 'file-firmware');
setupDropZone('drop-bootloader', 'file-bootloader');
setupDropZone('drop-partitions', 'file-partitions');
setupDropZone('drop-boot-app0', 'file-boot-app0');
setupDropZone('drop-app', 'file-app');
// ── 업로드 모드 토글 ─────────────────────────────────────────
const modeMerged = $('#mode-merged');
const modeSplit = $('#mode-split');
const splitFiles = $('#split-files');
const lblMerged = $('#lbl-merged');
const lblSplit = $('#lbl-split');
const firmwareHintText = $('#firmware-hint-text');
function applyUploadMode() {
const isMerged = modeMerged.checked;
splitFiles.style.display = isMerged ? 'none' : 'flex';
lblMerged.style.border = isMerged ? '2px solid var(--accent)' : '2px solid var(--border)';
lblMerged.style.background = isMerged ? 'rgba(0,200,150,.06)' : '';
lblSplit.style.border = isMerged ? '2px solid var(--border)' : '2px solid var(--accent)';
lblSplit.style.background = isMerged ? '' : 'rgba(0,200,150,.06)';
firmwareHintText.textContent = isMerged
? '*.merged.bin 파일을 드래그하거나 클릭하세요'
: '앱 바이너리 (*.ino.bin)를 드래그하거나 클릭하세요';
}
modeMerged.addEventListener('change', applyUploadMode);
modeSplit.addEventListener('change', applyUploadMode);
uploadForm.addEventListener('submit', async e => {
e.preventDefault();
const fwFile = $('#file-firmware').files[0];
if (!fwFile) {
alert('펌웨어(.bin) 파일을 선택하세요.');
return;
const isMerged = modeMerged.checked;
const fwFile = $('#file-firmware').files[0];
if (isMerged) {
if (!fwFile) { alert('merged.bin 파일을 선택하세요.'); return; }
} else {
const appFile = $('#file-app').files[0];
if (!fwFile && !appFile) { alert('앱 바이너리(.bin) 파일을 선택하세요.'); return; }
// 분리 모드에서는 app 드롭존 파일을 firmware로 사용
if (!fwFile && appFile) {
const dt = new DataTransfer();
dt.items.add(appFile);
$('#file-firmware').files = dt.files;
}
}
const finalFwFile = $('#file-firmware').files[0] || $('#file-app').files[0];
const fd = new FormData();
fd.append('name', $('#fw-name').value || fwFile.name.replace('.bin',''));
fd.append('version', $('#fw-version').value || '1.0.0');
fd.append('name', $('#fw-name').value || finalFwFile.name.replace(/\.bin$/i,''));
fd.append('version', $('#fw-version').value || '1.0.0');
fd.append('description', $('#fw-desc').value);
fd.append('chipFamily', $('#fw-chip').value);
fd.append('flashAddress', '0x10000');
fd.append('firmware', fwFile);
fd.append('chipFamily', $('#fw-chip').value);
fd.append('firmware', finalFwFile);
// flashAddress 미전송 → 서버가 bootloader 유무로 자동 결정 (merged:0x0 / split:0x10000)
const blFile = $('#file-bootloader').files[0];
if (blFile) fd.append('bootloader', blFile);
if (!isMerged) {
const blFile = $('#file-bootloader').files[0];
if (blFile) fd.append('bootloader', blFile);
const ptFile = $('#file-partitions').files[0];
if (ptFile) fd.append('partitions', ptFile);
const ptFile = $('#file-partitions').files[0];
if (ptFile) fd.append('partitions', ptFile);
const baFile = $('#file-boot-app0').files[0];
if (baFile) fd.append('boot_app0', baFile);
}
progressWrap.style.display = 'block';
progressBar.style.width = '0%';