33// found in the LICENSE file.
44
55import { ESP32 , Stub } from "./stubs" ;
6- import { isTransientError , sleep , toByteArray , toHex , Uint8Buffer , Uint8BufferSlipEncode } from "./util" ;
6+ import { sleep , toByteArray , toHex , Uint8Buffer , Uint8BufferSlipEncode } from "./util" ;
7+ import { UnknownChipFamilyError , ConnectError } from "./errors" ;
8+ import { Reader } from "./reader" ;
79
810export enum ChipFamily {
911 ESP32 = "esp32" ,
@@ -50,7 +52,7 @@ const ESP_RAM_BLOCK = 0x1800;
5052
5153// Timeouts
5254const DEFAULT_TIMEOUT = 3000 ; // timeout for most flash operations
53- const CHIP_ERASE_TIMEOUT = 300000 ; // timeout for full chip erase
55+ const CHIP_ERASE_TIMEOUT = 120000 ; // timeout for full chip erase
5456const MAX_TIMEOUT = CHIP_ERASE_TIMEOUT * 2 ; // longest any command can run
5557const SYNC_TIMEOUT = 100 ; // timeout for syncing with bootloader
5658const ERASE_REGION_TIMEOUT_PER_MB = 30000 ; // timeout (per megabyte) for erasing a region
@@ -75,12 +77,6 @@ interface commandResult {
7577
7678type progressCallback = ( i : number , total : number ) => void ;
7779
78- const UnknownChipFamilyError = "Unknown chip family" ;
79- const ClosedError = "closed" ;
80- const TimeoutError = "timeout" ;
81- const ConnectError = "connect error" ;
82- const ReadAlreadyInProgressError = "Read already in progress" ;
83-
8480export class EspLoader {
8581 // caches
8682 private _chipfamily : ChipFamily | undefined ;
@@ -92,8 +88,7 @@ export class EspLoader {
9288
9389 private baudRate = ESP_ROM_BAUD ;
9490
95- private serialReaderClosed = false ;
96- private serialReader : ReadableStreamDefaultReader < Uint8Array > | undefined = undefined ;
91+ private reader : Reader ;
9792
9893 constructor ( serialPort : SerialPort , options ?: Partial < EspLoaderOptions > ) {
9994 this . options = Object . assign (
@@ -104,6 +99,7 @@ export class EspLoader {
10499 } ,
105100 options || { }
106101 ) ;
102+ this . reader = new Reader ( ) ;
107103 this . serialPort = serialPort ;
108104 }
109105
@@ -125,8 +121,12 @@ export class EspLoader {
125121 * ESP ROM bootloader, we will retry a few times
126122 */
127123 async connect ( retries = 7 ) : Promise < void > {
124+ this . reader . start ( this . serialPort . readable ) ;
128125 let connected = false ;
129126 for ( let i = 0 ; i < retries ; i ++ ) {
127+ if ( i > 0 ) {
128+ this . options . logger . log ( "retrying..." ) ;
129+ }
130130 if ( await this . try_connect ( ) ) {
131131 connected = true ;
132132 break ;
@@ -137,61 +137,52 @@ export class EspLoader {
137137 throw ConnectError ;
138138 }
139139
140- await this . flushInput ( ) ;
140+ await this . reader . waitSilent ( 1 , 200 ) ;
141141 await this . chipFamily ( ) ;
142142 }
143143
144144 private async try_connect ( ) : Promise < boolean > {
145- await this . serialPort . setSignals ( { dataTerminalReady : false } ) ;
146- await this . serialPort . setSignals ( { requestToSend : true } ) ;
145+ await this . serialPort . setSignals ( { dataTerminalReady : false , requestToSend : true } ) ;
147146 await sleep ( 100 ) ;
148- await this . serialPort . setSignals ( { dataTerminalReady : true } ) ;
149- await this . serialPort . setSignals ( { requestToSend : false } ) ;
147+ await this . serialPort . setSignals ( { dataTerminalReady : true , requestToSend : false } ) ;
150148 await sleep ( 50 ) ;
151- await this . serialPort . setSignals ( { dataTerminalReady : false } ) ;
149+ await this . serialPort . setSignals ( { dataTerminalReady : false , requestToSend : false } ) ;
152150
153151 // Wait until device has stable output.
154- for ( let i = 0 ; i < 20 ; i ++ ) {
155- try {
156- await this . read ( false , 1000 ) ;
157- } catch ( e ) {
158- if ( e === TimeoutError ) {
159- break ;
160- }
161- }
162- await sleep ( 50 ) ;
152+ const wasSilent = await this . reader . waitSilent ( 20 , 1000 ) ;
153+ if ( ! wasSilent ) {
154+ this . options . logger . log ( "failed to enter bootloader" ) ;
155+ return false ;
163156 }
157+ this . options . logger . log ( "trying to sync with bootloader..." ) ;
164158
165159 // Try sync.
160+ this . options . logger . debug ( "sync started" ) ;
166161 for ( let i = 0 ; i < 7 ; i ++ ) {
167162 try {
168163 if ( await this . sync ( ) ) {
164+ this . options . logger . log ( "synced with bootloader" ) ;
169165 return true ;
170166 }
171167 } catch ( e ) {
172168 this . options . logger . debug ( "sync error" , e ) ;
173169 }
174170 await sleep ( 50 ) ;
175171 }
172+ this . options . logger . debug ( "sync stopped" ) ;
176173
174+ this . options . logger . log ( "failed to sync with bootloader" ) ;
177175 return false ;
178176 }
179177
180178 /**
181179 * shutdown the read loop.
182180 */
183181 async disconnect ( ) : Promise < void > {
184- const reader = this . serialReader ;
185- if ( reader ) {
186- try {
187- this . serialReaderClosed = true ;
188- await reader . cancel ( ) ;
189- await reader . closed ;
190- } catch ( e ) {
191- //ignore cancel errors.
192- }
182+ const err = await this . reader . stop ( ) ;
183+ if ( err !== undefined ) {
184+ throw err ;
193185 }
194- return ;
195186 }
196187
197188 async crystalFrequency ( ) : Promise < number > {
@@ -348,20 +339,24 @@ export class EspLoader {
348339 timeout : number = DEFAULT_TIMEOUT
349340 ) : Promise < number [ ] > {
350341 timeout = Math . min ( timeout , MAX_TIMEOUT ) ;
351- await this . sendCommand ( opcode , buffer , checksum ) ;
352- const resp = await this . getResponse ( opcode , timeout ) ;
353- const data = resp . data ;
354- const value = resp . value ;
355- if ( data . length > 4 ) {
356- return data ;
357- } else {
358- return value ;
342+ const unlisten = this . reader . listen ( ) ;
343+ try {
344+ await this . sendCommand ( opcode , buffer , checksum ) ;
345+ const resp = await this . getResponse ( opcode , timeout ) ;
346+ const data = resp . data ;
347+ const value = resp . value ;
348+ if ( data . length > 4 ) {
349+ return data ;
350+ } else {
351+ return value ;
352+ }
353+ } finally {
354+ unlisten ( ) ;
359355 }
360356 }
361357
362358 private _sendCommandBuffer = new Uint8BufferSlipEncode ( ) ;
363359 private async sendCommand ( opcode : number , buffer : Uint8Array , checksum = 0 ) {
364- this . readBuffer . reset ( ) ;
365360 const packet = this . _sendCommandBuffer ;
366361 packet . reset ( ) ;
367362 packet . push ( 0xc0 , 0x00 ) ; // direction
@@ -382,7 +377,7 @@ export class EspLoader {
382377
383378 private async getResponse ( opcode : number , timeout : number = DEFAULT_TIMEOUT ) : Promise < commandResult > {
384379 try {
385- const reply = await this . read ( true , timeout ) ;
380+ const reply = await this . reader . packet ( 12 , timeout ) ;
386381 if ( this . options . debug ) {
387382 this . logger . debug ( "Reading" , reply . length , "byte" + ( reply . length == 1 ? "" : "s" ) + ":" , reply ) ;
388383 }
@@ -425,32 +420,34 @@ export class EspLoader {
425420
426421 // Reopen the port and read loop
427422 await this . serialPort . open ( { baudRate : baud } ) ;
423+ this . reader . start ( this . serialPort . readable ) ;
428424 await sleep ( 50 ) ;
429- await this . flushInput ( ) ;
425+ const wasSilent = await this . reader . waitSilent ( 10 , 200 ) ;
426+ if ( ! wasSilent ) {
427+ this . logger . debug ( "after baud change reader was not silent" ) ;
428+ }
430429
431430 // Baud rate was changed
432431 this . logger . log ( "Changed baud rate to" , baud ) ;
433432 this . baudRate = baud ;
434433 }
435434
436- async flushInput ( ) : Promise < void > {
437- try {
438- this . readBuffer . reset ( ) ;
439- await this . read ( false , 200 ) ;
440- } catch ( e ) { }
441- }
442-
443435 /**
444436 * Put into ROM bootload mode & attempt to synchronize with the
445437 * ESP ROM bootloader, we will retry a few times
446438 */
447439 private async sync ( ) : Promise < boolean > {
448- await this . sendCommand ( ESP_SYNC , SYNC_PACKET ) ;
449- const { data } = await this . getResponse ( ESP_SYNC , SYNC_TIMEOUT ) ;
450- if ( data . length > 1 && data [ 0 ] == 0 && data [ 1 ] == 0 ) {
451- return true ;
440+ const unlisten = this . reader . listen ( ) ;
441+ try {
442+ await this . sendCommand ( ESP_SYNC , SYNC_PACKET ) ;
443+ const { data } = await this . getResponse ( ESP_SYNC , SYNC_TIMEOUT ) ;
444+ if ( data . length > 1 && data [ 0 ] == 0 && data [ 1 ] == 0 ) {
445+ return true ;
446+ }
447+ return false ;
448+ } finally {
449+ unlisten ( ) ;
452450 }
453- return false ;
454451 }
455452
456453 private getFlashWriteSize ( ) : number {
@@ -677,95 +674,23 @@ export class EspLoader {
677674 await writeMem ( stub . text , stub . textStart ) ;
678675 await writeMem ( stub . data , stub . dataStart ) ;
679676 this . logger . log ( "Running stub..." ) ;
680- await this . memFinish ( stub . entry ) ;
681-
682- const p = await this . read ( true , 1000 , 6 ) ;
683- const str = String . fromCharCode ( ...p ) ;
684- if ( str !== "OHAI" ) {
685- throw "Failed to start stub. Unexpected response: " + str ;
677+ const unlisten = this . reader . listen ( ) ;
678+ try {
679+ await this . memFinish ( stub . entry ) ;
680+ const p = await this . reader . packet ( 6 , 1000 ) ;
681+ const str = String . fromCharCode ( ...p ) ;
682+ if ( str !== "OHAI" ) {
683+ throw "Failed to start stub. Unexpected response: " + str ;
684+ }
685+ } finally {
686+ unlisten ( ) ;
686687 }
687688 this . logger . log ( "Stub is now running..." ) ;
688689 this . isStub = true ;
689690 this . _chipfamily = undefined ;
690691 this . _efuses = undefined ;
691692 }
692693
693- private readBuffer = new Uint8BufferSlipEncode ( ) ;
694- private async read ( packetMode = true , timeoutMs = 1000 , minRead = 12 ) : Promise < Uint8Array > {
695- if ( this . serialReader !== undefined ) {
696- throw ReadAlreadyInProgressError ;
697- }
698- let reader = this . serialPort . readable . getReader ( ) ;
699- this . serialReader = reader ;
700- this . serialReaderClosed = false ;
701- const chTimeout = setTimeout ( async ( ) => {
702- try {
703- this . serialReaderClosed = true ;
704- await reader . cancel ( ) ;
705- } catch ( e ) {
706- // Ignore cancel errors.
707- }
708- } , timeoutMs ) ;
709-
710- try {
711- while ( true ) {
712- if ( this . serialReaderClosed ) {
713- throw TimeoutError ;
714- }
715-
716- try {
717- return await this . _read ( reader , packetMode , minRead ) ;
718- } catch ( e ) {
719- if ( e === ClosedError ) {
720- reader . releaseLock ( ) ;
721- await sleep ( 1 ) ;
722- reader = this . serialPort . readable . getReader ( ) ;
723- this . serialReader = reader ;
724- } else if ( ! isTransientError ( e ) ) {
725- throw e ;
726- }
727- }
728- }
729- } finally {
730- clearTimeout ( chTimeout ) ;
731- try {
732- await reader . cancel ( ) ;
733- } catch ( e ) {
734- // ignore cancel errors.
735- }
736- reader . releaseLock ( ) ;
737- this . serialReader = undefined ;
738- this . serialReaderClosed = false ;
739- }
740- }
741-
742- private async _read (
743- reader : ReadableStreamDefaultReader < Uint8Array > ,
744- packetMode = true ,
745- minRead = 12
746- ) : Promise < Uint8Array > {
747- while ( true ) {
748- if ( this . readBuffer . length >= minRead ) {
749- if ( packetMode ) {
750- const res = this . readBuffer . packet ( true ) ;
751- if ( res !== undefined ) {
752- return res ;
753- }
754- } else {
755- return this . readBuffer . view ( ) ;
756- }
757- }
758-
759- const { value, done } = await reader . read ( ) ;
760- if ( value ) {
761- this . readBuffer . copy ( value ) ;
762- }
763- if ( done ) {
764- throw ClosedError ;
765- }
766- }
767- }
768-
769694 /**
770695 * erase the flash of the device
771696 *
0 commit comments