feat: add LilyGo T-Beam and T-Beam Supreme board support

This commit is contained in:
swaits committed 2026-04-23 13:07:17 -06:00
1 parent 389b6354f7
commit 6486ce1f91
6 files changed
+64 -8

No files matched your search

+18
View File
@@ -9,6 +9,7 @@
let { selected, onselect }: Props = $props();
const esp32Boards = boards.filter((b) => b.family === "esp32");
const espBoards = boards.filter((b) => b.family === "esp32s3");
const rpBoards = boards.filter((b) => b.family === "rp2040");
const nrfBoards = boards.filter((b) => b.family === "nrf52840");
@@ -39,6 +40,23 @@
<div>
<h3 class="font-mono text-sm text-text-muted mb-3 uppercase tracking-wider">Select Board</h3>
<p class="text-xs text-text-dim mb-2 font-mono">ESP32</p>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3 mb-4">
{#each esp32Boards as board}
<button
class="text-left bg-bg-card border p-4 rounded transition-all cursor-pointer
{selected?.id === board.id
? 'border-accent shadow-[0_0_12px_oklch(from_var(--color-accent)_l_c_h_/_0.15)]'
: 'border-border hover:border-border-bright'}"
onclick={() => onselect(board)}
>
<div class="font-mono font-medium text-text text-sm">{board.name}</div>
<div class="text-xs text-text-muted mt-1">{board.mcu} &middot; {board.radio}</div>
<div class="text-xs {badgeColor(board)} mt-2 font-mono">{badgeText(board)}</div>
</button>
{/each}
</div>
<p class="text-xs text-text-dim mb-2 font-mono">ESP32-S3</p>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3 mb-4">
{#each espBoards as board}
+1 -1
View File
@@ -38,7 +38,7 @@
let session;
try {
session = await connect(callbacks);
session = await connect(callbacks, board.family === "esp32" ? "esp32" : "esp32s3");
} catch (err) {
const msg = err instanceof DOMException && err.name === "NotFoundError"
? "Port selection cancelled."
+2 -2
View File
@@ -59,7 +59,7 @@
{#if selectedBoard && selectedRelease}
<div class="border-t border-border pt-8">
<h3 class="font-mono text-sm text-text-muted mb-3 uppercase tracking-wider">
{#if selectedBoard.family === "esp32s3"}
{#if (selectedBoard.family === "esp32" || selectedBoard.family === "esp32s3")}
Flash Firmware
{:else}
Download Firmware
@@ -72,7 +72,7 @@
</div>
{/if}
{#if selectedBoard.family === "esp32s3"}
{#if (selectedBoard.family === "esp32" || selectedBoard.family === "esp32s3")}
<EspFlashFlow board={selectedBoard} release={selectedRelease} />
{:else}
<Uf2DownloadFlow board={selectedBoard} release={selectedRelease} />
+22
View File
@@ -1,6 +1,17 @@
import type { BoardDef } from "./types";
export const boards: BoardDef[] = [
{
id: "lilygo_tbeam",
name: "LilyGo T-Beam (classic)",
mcu: "ESP32",
radio: "SX1276",
family: "esp32",
flashMethod: "web-serial",
assetPattern: "donglora-lilygo_tbeam-v{version}.bin",
notes:
"Original T-Beam with ESP32 + SX1276 + SSD1306 OLED + AXP192 PMIC. Flashes over the on-board CP2102 USB-to-UART bridge. Clients connect via serial (BLE planned).",
},
{
id: "elecrow_thinknode_m2",
name: "ELECROW ThinkNode-M2",
@@ -43,6 +54,17 @@ export const boards: BoardDef[] = [
flashMethod: "web-serial",
assetPattern: "donglora-heltec_v4-v{version}.bin",
},
{
id: "lilygo_tbeam_supreme",
name: "LilyGo T-Beam S3 Supreme",
mcu: "ESP32-S3",
radio: "SX1262",
family: "esp32s3",
flashMethod: "web-serial",
assetPattern: "donglora-lilygo_tbeam_supreme-v{version}.bin",
notes:
"T-Beam Supreme with ESP32-S3 + SX1262 + SH1106 OLED + AXP2101 PMIC. Flashes over native USB CDC-ACM — no UART bridge.",
},
{
id: "rak_wisblock_4631",
name: "RAK WisBlock 4631",
+20 -4
View File
@@ -1,5 +1,7 @@
import type { FlashProgress } from "./types";
export type EspChipFamily = "esp32" | "esp32s3";
export function isWebSerialSupported(): boolean {
return typeof navigator !== "undefined" && "serial" in navigator;
}
@@ -19,7 +21,20 @@ async function loadEsptool() {
return { ESPLoader: mod.ESPLoader, Transport: mod.Transport };
}
export async function connect(callbacks: FlashCallbacks): Promise<FlashSession> {
function chipFamilyLabel(family: EspChipFamily): string {
return family === "esp32s3" ? "ESP32-S3" : "ESP32";
}
function isChipCompatible(detected: string, expected: EspChipFamily): boolean {
if (expected === "esp32s3") return detected.includes("ESP32-S3");
// Classic ESP32: esptool-js reports "ESP32". Exclude S2/S3/C3/C6/H2/P4 variants.
return /^ESP32(?![-\s]?(S|C|H|P)\d)/i.test(detected);
}
export async function connect(
callbacks: FlashCallbacks,
expectedChip: EspChipFamily,
): Promise<FlashSession> {
if (!isWebSerialSupported()) {
throw new Error("Web Serial API is not supported in this browser. Use Chrome, Edge, or Opera.");
}
@@ -45,16 +60,17 @@ export async function connect(callbacks: FlashCallbacks): Promise<FlashSession>
},
});
callbacks.onLog("Connecting to ESP32...");
const expectedLabel = chipFamilyLabel(expectedChip);
callbacks.onLog(`Connecting to ${expectedLabel}...`);
callbacks.onProgress({ phase: "Connecting", percent: 0, message: "Detecting chip..." });
await loader.main();
const chip = loader.chip.CHIP_NAME;
callbacks.onLog(`Detected: ${chip}`);
if (!chip.includes("ESP32-S3")) {
if (!isChipCompatible(chip, expectedChip)) {
await transport.disconnect();
throw new Error(`Expected ESP32-S3 but detected ${chip}. Make sure you selected the correct board.`);
throw new Error(`Expected ${expectedLabel} but detected ${chip}. Make sure you selected the correct board.`);
}
return { transport, loader };
+1 -1
View File
@@ -1,4 +1,4 @@
export type BoardFamily = "esp32s3" | "nrf52840" | "rp2040";
export type BoardFamily = "esp32" | "esp32s3" | "nrf52840" | "rp2040";
export type FlashMethod = "web-serial" | "uf2" | "download-only";
export interface BoardDef {