- DB720Flasher: WPF .NET 9 GUI 플래셔 - 멀티모델 프로파일(flash.json + 빌트인 DB720/DB402S/DB400S/DB400Q/DB300eM) - 포트 자동감지(JTAG 우선, 유령 포트 자동 회피), esptool v5 동봉 - flash_app.ps1 / flash_full.ps1 / list_ports.ps1: 기존 PowerShell 키트 - profiles/: 모델별 flash.json 템플릿 - firmware/flash.json: DB720 모델 설정 (서명 실펌웨어 .bin 은 repo 제외) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
104 lines
5.0 KiB
C#
104 lines
5.0 KiB
C#
using System.IO;
|
|
using System.Text.Json;
|
|
|
|
namespace DB720Flasher;
|
|
|
|
/// <summary>빌트인 모델 프로파일 + 폴더 flash.json 로더 + 앱 파일명 자동매칭.</summary>
|
|
public static class ProfileStore
|
|
{
|
|
static readonly JsonSerializerOptions JsonOpts = new()
|
|
{
|
|
PropertyNameCaseInsensitive = true,
|
|
ReadCommentHandling = JsonCommentHandling.Skip,
|
|
AllowTrailingCommas = true,
|
|
};
|
|
|
|
static FwRegion Boot(string off) => new() { File = "bootloader.bin", Offset = off, Kind = "bootloader" };
|
|
static FwRegion Part() => new() { File = "partition-table.bin", Offset = "0x10000", Kind = "support" };
|
|
static FwRegion Ota() => new() { File = "ota_data_initial.bin", Offset = "0x11000", Kind = "support" };
|
|
static FwRegion Www() => new() { File = "www_fs.bin", Offset = "0x420000", Kind = "support" };
|
|
static FwRegion AppAt(string off = "0x20000") => new() { App = true, Offset = off, Kind = "app" };
|
|
|
|
public static List<FlashProfile> BuiltIns() => new()
|
|
{
|
|
new FlashProfile
|
|
{
|
|
Name = "DB720 (ESP32-P4)", Chip = "esp32p4", Baud = 115200, NoStub = true, PerRegion = true,
|
|
ConnectAttempts = 30, FlashSize = "16MB", FlashMode = "dio", FlashFreq = "80m",
|
|
AppOnlyErasesOtadata = true,
|
|
Regions = { Boot("0x2000"), Part(), Ota(), Www(), AppAt() },
|
|
FlashPortHint = "303A:1001", ConsolePortHint = "10C4:EA60",
|
|
DownloadModeHint = "USB-JTAG 케이블 연결. 포트가 안 잡히면 케이블만 다시 꽂으면 됩니다(자동 감지).",
|
|
Notes = "secure_release · db250513 서명. 영역별 개별 호출(SDM). 부트로더(0x2000)는 이미 구워진 보드면 거부될 수 있음(정상 → 표준/앱만 사용).",
|
|
},
|
|
new FlashProfile
|
|
{
|
|
Name = "DB402S (ESP32-S3)", Chip = "esp32s3", Baud = 921600,
|
|
Regions = { Boot("0x0000"), Part(), Ota(), AppAt() },
|
|
ConsolePortHint = "10C4:EA60",
|
|
DownloadModeHint = "TTL(COM2) 연결. SW1 누른 상태에서 SW2 눌렀다 떼고 SW1 떼면 다운로드 모드 진입.",
|
|
Notes = "esp32-s3. 부트로더 offset 0x0000(중요). 스텁 921600 · 한 번에 기록. secure 보드는 부트로더(<0x8000) 거부 → --force 또는 앱만.",
|
|
},
|
|
new FlashProfile
|
|
{
|
|
Name = "DB400S (ESP32)", Chip = "esp32", Baud = 921600,
|
|
Regions = { Boot("0x1000"), Part(), Ota(), AppAt() },
|
|
ConsolePortHint = "10C4:EA60",
|
|
DownloadModeHint = "TTL(COM2) 연결. SW1 누른 상태에서 SW2 눌렀다 떼고 SW1 떼면 다운로드 모드 진입.",
|
|
Notes = "esp32 클래식. 부트로더 0x1000. 스텁 921600 · 한 번에. secure 보드는 부트로더(<0x8000) 거부 → --force 또는 앱만.",
|
|
},
|
|
new FlashProfile
|
|
{
|
|
Name = "DB400Q (ESP32)", Chip = "esp32", Baud = 921600,
|
|
Regions = { Boot("0x1000"), Part(), Ota(), AppAt() },
|
|
ConsolePortHint = "10C4:EA60",
|
|
DownloadModeHint = "TTL(COM2) 연결. SW1 누른 상태에서 SW2 눌렀다 떼고 SW1 떼면 다운로드 모드 진입.",
|
|
Notes = "esp32 클래식. DB400S와 동일 방식.",
|
|
},
|
|
new FlashProfile
|
|
{
|
|
Name = "DB300eM (ESP32)", Chip = "esp32", Baud = 921600,
|
|
Regions = { Boot("0x1000"), Part(), Ota(), AppAt() },
|
|
ConsolePortHint = "10C4:EA60",
|
|
DownloadModeHint = "TTL(COM2) 연결. JP1 점퍼핀 단락 후 전원 재인가하면 다운로드 모드 진입.",
|
|
Notes = "esp32 클래식. JP1 점퍼로 다운로드 진입. web OTA(IP 입력)도 가능.",
|
|
},
|
|
};
|
|
|
|
/// <summary>폴더의 flash.json 을 읽는다(없으면 null).</summary>
|
|
public static FlashProfile? LoadFromFolder(string folder)
|
|
{
|
|
try
|
|
{
|
|
var p = Path.Combine(folder, "flash.json");
|
|
if (!File.Exists(p)) return null;
|
|
var prof = JsonSerializer.Deserialize<FlashProfile>(File.ReadAllText(p), JsonOpts);
|
|
if (prof is not null) prof.FromFile = true;
|
|
return prof;
|
|
}
|
|
catch { return null; }
|
|
}
|
|
|
|
/// <summary>앱 bin 파일명(DIBD720…, DIBD402S… 등)으로 빌트인 프로파일을 추정.</summary>
|
|
public static FlashProfile? MatchByApp(IEnumerable<string> appFileNames, List<FlashProfile> profiles)
|
|
{
|
|
// 더 구체적인 토큰을 먼저 (402S 가 400 보다, 400S 가 400 보다 우선)
|
|
(string token, string profileTag)[] map =
|
|
{
|
|
("402S", "DB402S"), ("400S", "DB400S"), ("400Q", "DB400Q"),
|
|
("300", "DB300"), ("720", "DB720"),
|
|
};
|
|
foreach (var name in appFileNames)
|
|
{
|
|
var up = name.ToUpperInvariant();
|
|
foreach (var (token, tag) in map)
|
|
if (up.Contains(token))
|
|
{
|
|
var hit = profiles.FirstOrDefault(p => p.Name.ToUpperInvariant().Contains(tag));
|
|
if (hit is not null) return hit;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|