Skip to content

Commit 66b9e3a

Browse files
author
3rdIteration
committed
feat: add serial monitor with auto-connect after flashing
1 parent d67604d commit 66b9e3a

1 file changed

Lines changed: 112 additions & 2 deletions

File tree

flasher/index.html

Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ <h1>Jade Web Flasher</h1>
171171

172172
<div class="actions">
173173
<button class="btn btn-primary" id="flashBtn">Flash Firmware</button>
174+
<button class="btn btn-outline" id="serialBtn">Connect Serial</button>
175+
<button class="btn btn-outline" id="disconnectBtn" style="display:none">Disconnect</button>
174176
</div>
175177

176178
<div id="progressSection" style="display:none">
@@ -180,6 +182,10 @@ <h1>Jade Web Flasher</h1>
180182
<div class="console" id="console">
181183
<span class="info">Ready. Select a firmware source and click "Flash Firmware".</span>
182184
</div>
185+
186+
<div class="console" id="serialOutput" style="display:none; max-height: 400px;">
187+
<span class="info">Serial monitor connected (115200 baud). Output appears below.</span>
188+
</div>
183189
</div>
184190

185191
<script src="vendor/jszip-3.10.1.min.js"></script>
@@ -263,6 +269,102 @@ <h1>Jade Web Flasher</h1>
263269
});
264270

265271
let zipFirmware = null;
272+
let serialMonitor = null;
273+
let serialPort = null;
274+
275+
const serialDiv = document.getElementById('serialOutput');
276+
const consoleDiv = document.getElementById('console');
277+
const serialBtn = document.getElementById('serialBtn');
278+
const disconnectBtn = document.getElementById('disconnectBtn');
279+
280+
function serialWrite(text) {
281+
const last = serialDiv.lastChild;
282+
if (last && last.nodeType === Node.TEXT_NODE) {
283+
last.textContent += text;
284+
} else {
285+
serialDiv.appendChild(document.createTextNode(text));
286+
}
287+
serialDiv.scrollTop = serialDiv.scrollHeight;
288+
if (serialDiv.children.length > 500) {
289+
serialDiv.removeChild(serialDiv.firstChild);
290+
}
291+
}
292+
293+
function serialLine(text, cls = '') {
294+
const line = document.createElement('div');
295+
if (cls) line.className = cls;
296+
line.textContent = text + '\n';
297+
serialDiv.appendChild(line);
298+
serialDiv.scrollTop = serialDiv.scrollHeight;
299+
if (serialDiv.children.length > 500) {
300+
serialDiv.removeChild(serialDiv.firstChild);
301+
}
302+
}
303+
304+
async function startSerialMonitor(port) {
305+
serialPort = port;
306+
serialDiv.style.display = 'block';
307+
consoleDiv.style.display = 'none';
308+
serialDiv.innerHTML = '';
309+
310+
serialBtn.style.display = 'none';
311+
disconnectBtn.style.display = '';
312+
document.getElementById('flashBtn').disabled = true;
313+
314+
serialLine('Serial monitor connected (115200 baud). Press RESET on device if needed.', 'info');
315+
316+
let reader;
317+
try {
318+
reader = port.readable.getReader();
319+
} catch (e) {
320+
serialLine(`Failed to open serial stream: ${e.message}`, 'err');
321+
await stopSerialMonitor();
322+
return;
323+
}
324+
const decoder = new TextDecoder();
325+
serialMonitor = { port, reader };
326+
327+
try {
328+
while (serialMonitor) {
329+
const { value, done } = await reader.read();
330+
if (done || !serialMonitor) break;
331+
const text = decoder.decode(value, { stream: true });
332+
serialWrite(text);
333+
}
334+
} catch (e) {
335+
if (serialMonitor) serialLine(`Serial disconnected: ${e.message}`, 'err');
336+
}
337+
}
338+
339+
async function stopSerialMonitor() {
340+
if (serialMonitor) {
341+
const { port, reader } = serialMonitor;
342+
serialMonitor = null;
343+
try { await reader.cancel(); } catch (_) {}
344+
try { reader.releaseLock(); } catch (_) {}
345+
try { await port.close(); } catch (_) {}
346+
} else if (serialPort) {
347+
try { await serialPort.close(); } catch (_) {}
348+
}
349+
serialPort = null;
350+
serialDiv.style.display = 'none';
351+
consoleDiv.style.display = '';
352+
serialBtn.style.display = '';
353+
disconnectBtn.style.display = 'none';
354+
document.getElementById('flashBtn').disabled = false;
355+
}
356+
357+
serialBtn.addEventListener('click', async () => {
358+
try {
359+
const port = await navigator.serial.requestPort();
360+
await port.open({ baudRate: 115200 });
361+
await startSerialMonitor(port);
362+
} catch (e) {
363+
log('Serial connect failed: ' + e.message, 'err');
364+
}
365+
});
366+
367+
disconnectBtn.addEventListener('click', stopSerialMonitor);
266368

267369
async function handleZip(file) {
268370
clearLog();
@@ -399,6 +501,7 @@ <h1>Jade Web Flasher</h1>
399501
log('\nConnecting to device...');
400502
log('Select the device serial port in the browser prompt.');
401503

504+
document.getElementById('serialBtn').disabled = true;
402505
let transport;
403506
try {
404507
const port = await navigator.serial.requestPort();
@@ -450,13 +553,20 @@ <h1>Jade Web Flasher</h1>
450553

451554
log('Resetting device...');
452555
await esploader.after('hard_reset');
453-
log('Done! Device should reboot.', 'ok');
556+
557+
log('Starting serial monitor (115200 baud)...', 'ok');
558+
await transport.disconnect();
559+
await port.open({ baudRate: 115200 });
560+
await startSerialMonitor(port);
454561

455562
} catch (e) {
456563
log(`\nError: ${e.message}`, 'err');
457564
} finally {
458565
btn.disabled = false;
459-
try { if (transport) await transport.disconnect(); } catch (_) {}
566+
document.getElementById('serialBtn').disabled = false;
567+
if (!serialMonitor) {
568+
try { if (transport) await transport.disconnect(); } catch (_) {}
569+
}
460570
}
461571
});
462572

0 commit comments

Comments
 (0)