@@ -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
265271let 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
267369async 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