@@ -7,8 +7,8 @@ import { html } from 'https://cdn.jsdelivr.net/npm/lit-html/+esm';
77import { map } from 'https://cdn.jsdelivr.net/npm/lit-html/directives/map/+esm' ;
88import * as toml from "https://cdn.jsdelivr.net/npm/iarna-toml-esm@3.0.5/+esm"
99import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.6.65/+esm" ;
10- import * as esptoolPackage from "https://cdn.jsdelivr.net/npm/esp-web-flasher@5 .1.2/dist/web/index.js/ +esm"
11- import { REPL } from 'https://cdn.jsdelivr.net/gh/adafruit/circuitpython-repl-js/repl.js' ;
10+ import { default as CryptoJS } from "https://cdn.jsdelivr.net/npm/crypto-js@4 .1.1/ +esm" ;
11+ import { REPL } from 'https://cdn.jsdelivr.net/gh/adafruit/circuitpython-repl-js@3.2.1 /repl.js' ;
1212import { InstallButton , ESP_ROM_BAUD } from "./base_installer.js" ;
1313
1414// TODO: Combine multiple steps together. For now it was easier to make them separate,
@@ -19,19 +19,15 @@ import { InstallButton, ESP_ROM_BAUD } from "./base_installer.js";
1919//
2020// TODO: Hide the log and make it accessible via the menu (future feature, output to console for now)
2121// May need to deal with the fact that the ESPTool uses Web Serial and CircuitPython REPL uses Web Serial
22+ //
23+ // TODO: Update File Operations to take advantage of the REPL FileOps class to allow non-CIRCUITPY drive access
2224
2325const PREFERRED_BAUDRATE = 921600 ;
2426const COPY_CHUNK_SIZE = 64 * 1024 ; // 64 KB Chunks
2527const DEFAULT_RELEASE_LATEST = false ; // Use the latest release or the stable release if not specified
2628const BOARD_DEFS = "https://adafruit-circuit-python.s3.amazonaws.com/esp32_boards.json" ;
2729
2830const CSS_DIALOG_CLASS = "cp-installer-dialog" ;
29- const FAMILY_TO_CHIP_MAP = {
30- 'esp32s2' : esptoolPackage . CHIP_FAMILY_ESP32S2 ,
31- 'esp32s3' : esptoolPackage . CHIP_FAMILY_ESP32S3 ,
32- 'esp32c3' : esptoolPackage . CHIP_FAMILY_ESP32C3 ,
33- 'esp32' : esptoolPackage . CHIP_FAMILY_ESP32
34- }
3531
3632const attrMap = {
3733 "bootloader" : "bootloaderUrl" ,
@@ -214,12 +210,12 @@ export class CPInstallButton extends InstallButton {
214210 isEnabled : async ( ) => { return ! this . hasNativeUsb ( ) && ! ! this . binFileUrl } ,
215211 } ,
216212 uf2Only : { // Upgrade when Bootloader is already installer
217- label : `Upgrade/ Install CircuitPython [version] UF2 Only` ,
213+ label : `Install CircuitPython [version] UF2 Only` ,
218214 steps : [ this . stepWelcome , this . stepSelectBootDrive , this . stepCopyUf2 , this . stepSelectCpyDrive , this . stepCredentials , this . stepSuccess ] ,
219215 isEnabled : async ( ) => { return this . hasNativeUsb ( ) && ! ! this . uf2FileUrl } ,
220216 } ,
221217 binOnly : {
222- label : `Upgrade CircuitPython [version] Bin Only` ,
218+ label : `Install CircuitPython [version] Bin Only` ,
223219 steps : [ this . stepWelcome , this . stepSerialConnect , this . stepConfirm , this . stepEraseAll , this . stepFlashBin , this . stepSuccess ] ,
224220 isEnabled : async ( ) => { return ! ! this . binFileUrl } ,
225221 } ,
@@ -311,6 +307,10 @@ export class CPInstallButton extends InstallButton {
311307 ` ,
312308 buttons : [
313309 this . previousButton ,
310+ {
311+ label : "Skip Erase" ,
312+ onClick : async ( e ) => { if ( confirm ( "Skipping the erase step may cause issues and is not recommended. Continue?" ) ) { await this . advanceSteps ( 2 ) ; } } ,
313+ } ,
314314 {
315315 label : "Continue" ,
316316 onClick : this . nextStep ,
@@ -481,7 +481,7 @@ export class CPInstallButton extends InstallButton {
481481 action : "Erasing Flash" ,
482482 } ) ;
483483 try {
484- await this . espStub . eraseFlash ( ) ;
484+ await this . esploader . eraseFlash ( ) ;
485485 } catch ( err ) {
486486 this . errorMsg ( "Unable to finish erasing Flash memory. Please try again." ) ;
487487 }
@@ -550,8 +550,8 @@ export class CPInstallButton extends InstallButton {
550550
551551 async stepSetupRepl ( ) {
552552 // TODO: Try and reuse the existing connection so user doesn't need to select it again
553- /*if (this.port ) {
554- this.replSerialDevice = this.port ;
553+ /*if (this.device ) {
554+ this.replSerialDevice = this.device ;
555555 await this.setupRepl();
556556 }*/
557557 const serialPortName = await this . getSerialPortName ( ) ;
@@ -579,6 +579,7 @@ export class CPInstallButton extends InstallButton {
579579 // TODO: Currently the control is just disabled and not used because we don't have anything to modify boot.py in place.
580580 // Setting mass_storage_disabled to true/false will display the checkbox with the appropriately checked state.
581581 //parameters.mass_storage_disabled = true;
582+ // This can be updated to use FileOps for ease of implementation
582583 }
583584
584585 // Display Credentials Request Dialog
@@ -658,45 +659,34 @@ export class CPInstallButton extends InstallButton {
658659 async espToolConnectHandler ( e ) {
659660 await this . onReplDisconnected ( e ) ;
660661 await this . espDisconnect ( ) ;
661- let esploader ;
662+ await this . setBaudRateIfChipSupports ( PREFERRED_BAUDRATE ) ;
662663 try {
663- esploader = await this . espConnect ( {
664+ this . updateEspConnected ( this . connectionStates . CONNECTING ) ;
665+ await this . espConnect ( {
664666 log : ( ...args ) => this . logMsg ( ...args ) ,
665667 debug : ( ...args ) => { } ,
666668 error : ( ...args ) => this . errorMsg ( ...args ) ,
667669 } ) ;
668- } catch ( err ) {
669- // It's possible the dialog was also canceled here
670- this . errorMsg ( "Unable to open Serial connection to board. Make sure the port is not already in use by another application or in another browser tab." ) ;
671- return ;
672- }
673-
674- try {
675- this . updateEspConnected ( this . connectionStates . CONNECTING ) ;
676- await esploader . initialize ( ) ;
677670 this . updateEspConnected ( this . connectionStates . CONNECTED ) ;
678671 } catch ( err ) {
679- await esploader . disconnect ( ) ;
680- // Disconnection before complete
672+ // It's possible the dialog was also canceled here
681673 this . updateEspConnected ( this . connectionStates . DISCONNECTED ) ;
682- this . errorMsg ( "Unable to connect to the board. Make sure it is in bootloader mode by holding the boot0 button when powering on and try again ." )
674+ this . errorMsg ( "Unable to open Serial connection to board. Make sure the port is not already in use by another application or in another browser tab ." ) ;
683675 return ;
684676 }
685677
686678 try {
687- this . logMsg ( `Connected to ${ esploader . chipName } ` ) ;
688- this . logMsg ( `MAC Address: ${ this . formatMacAddr ( esploader . macAddr ( ) ) } ` ) ;
679+ this . logMsg ( `Connected to ${ this . esploader . chip . CHIP_NAME } ` ) ;
689680
690681 // check chip compatibility
691- if ( FAMILY_TO_CHIP_MAP [ this . chipFamily ] == esploader . chipFamily ) {
682+ if ( this . chipFamily == ` ${ this . esploader . chip . CHIP_NAME } ` . toLowerCase ( ) . replaceAll ( "-" , "" ) ) {
692683 this . logMsg ( "This chip checks out" ) ;
693- this . espStub = await esploader . runStub ( ) ;
694- this . espStub . addEventListener ( "disconnect" , ( ) => {
695- this . updateEspConnected ( this . connectionStates . DISCONNECTED ) ;
696- this . espStub = null ;
697- } ) ;
698684
699- await this . setBaudRateIfChipSupports ( esploader . chipFamily , PREFERRED_BAUDRATE ) ;
685+ // esploader-js doesn't have a disconnect event, so we can't use this
686+ //this.esploader.addEventListener("disconnect", () => {
687+ // this.updateEspConnected(this.connectionStates.DISCONNECTED);
688+ //});
689+
700690 await this . nextStep ( ) ;
701691 return ;
702692 }
@@ -706,7 +696,9 @@ export class CPInstallButton extends InstallButton {
706696 await this . espDisconnect ( ) ;
707697
708698 } catch ( err ) {
709- await esploader . disconnect ( ) ;
699+ if ( this . transport ) {
700+ await this . transport . disconnect ( ) ;
701+ }
710702 // Disconnection before complete
711703 this . updateEspConnected ( this . connectionStates . DISCONNECTED ) ;
712704 this . errorMsg ( "Oops, we lost connection to your board before completing the install. Please check your USB connection and click Connect again. Refresh the browser if it becomes unresponsive." )
@@ -1024,10 +1016,28 @@ export class CPInstallButton extends InstallButton {
10241016
10251017 async downloadAndInstall ( url , fileToExtract = null , cacheFile = false ) {
10261018 let [ filename , fileBlob ] = await this . downloadAndExtract ( url , fileToExtract , cacheFile ) ;
1019+ const fileArray = [ ] ;
1020+
1021+ const readBlobAsBinaryString = ( inputFile ) => {
1022+ const reader = new FileReader ( ) ;
1023+
1024+ return new Promise ( ( resolve , reject ) => {
1025+ reader . onerror = ( ) => {
1026+ reader . abort ( ) ;
1027+ reject ( new DOMException ( "Problem parsing input file." ) ) ;
1028+ } ;
1029+
1030+ reader . onload = ( ) => {
1031+ resolve ( reader . result ) ;
1032+ } ;
1033+ reader . readAsBinaryString ( inputFile ) ;
1034+ } ) ;
1035+ } ;
10271036
10281037 // Update the Progress dialog
10291038 if ( fileBlob ) {
1030- const fileContents = ( new Uint8Array ( await fileBlob . arrayBuffer ( ) ) ) . buffer ;
1039+ fileArray . push ( { data : await readBlobAsBinaryString ( fileBlob ) , address : 0 } ) ;
1040+
10311041 let lastPercent = 0 ;
10321042 this . showDialog ( this . dialogs . actionProgress , {
10331043 action : `Flashing ${ filename } `
@@ -1037,14 +1047,22 @@ export class CPInstallButton extends InstallButton {
10371047 progressElement . value = 0 ;
10381048
10391049 try {
1040- await this . espStub . flashData ( fileContents , ( bytesWritten , totalBytes ) => {
1041- let percentage = Math . round ( ( bytesWritten / totalBytes ) * 100 ) ;
1042- if ( percentage > lastPercent ) {
1043- progressElement . value = percentage ;
1044- this . logMsg ( `${ percentage } % (${ bytesWritten } /${ totalBytes } )...` ) ;
1045- lastPercent = percentage ;
1046- }
1047- } , 0 , 0 ) ;
1050+ const flashOptions = {
1051+ fileArray : fileArray ,
1052+ flashSize : "keep" ,
1053+ eraseAll : false ,
1054+ compress : true ,
1055+ reportProgress : ( fileIndex , written , total ) => {
1056+ let percentage = Math . round ( ( written / total ) * 100 ) ;
1057+ if ( percentage > lastPercent ) {
1058+ progressElement . value = percentage ;
1059+ this . logMsg ( `${ percentage } % (${ written } /${ total } )...` ) ;
1060+ lastPercent = percentage ;
1061+ }
1062+ } ,
1063+ calculateMD5Hash : ( image ) => CryptoJS . MD5 ( CryptoJS . enc . Latin1 . parse ( image ) ) ,
1064+ } ;
1065+ await this . esploader . writeFlash ( flashOptions ) ;
10481066 } catch ( err ) {
10491067 this . errorMsg ( `Unable to flash file: ${ filename } . Error Message: ${ err } ` ) ;
10501068 }
@@ -1256,20 +1274,6 @@ export class CPInstallButton extends InstallButton {
12561274 return { } ;
12571275 }
12581276
1259- async espDisconnect ( ) {
1260- // Disconnect the ESPTool
1261- if ( this . espStub ) {
1262- await this . espStub . disconnect ( ) ;
1263- this . espStub . removeEventListener ( "disconnect" , this . espDisconnect . bind ( this ) ) ;
1264- this . updateEspConnected ( this . connectionStates . DISCONNECTED ) ;
1265- this . espStub = null ;
1266- }
1267- if ( this . port ) {
1268- await this . port . close ( ) ;
1269- this . port = null ;
1270- }
1271- }
1272-
12731277 async serialTransmit ( msg ) {
12741278 const encoder = new TextEncoder ( ) ;
12751279 if ( this . writer ) {
0 commit comments