2022-07-08 17:54:19 +00:00
import { ESPError , TimeoutError } from "./error.js" ;
2021-04-18 17:46:46 +00:00
2022-07-08 06:48:22 +00:00
const MAGIC _TO _CHIP = {
[ 0x00f01d83 ] : ( ) => import ( './targets/esp32.js' ) ,
2022-07-08 07:20:05 +00:00
[ 0x6921506f ] : ( ) => import ( './targets/esp32c3.js' ) , // ESP32C3 eco 1+2
[ 0x1b31506f ] : ( ) => import ( './targets/esp32c3.js' ) , // ESP32C3 eco3
2022-07-08 06:48:22 +00:00
[ 0x09 ] : ( ) => import ( './targets/esp32s3.js' ) ,
[ 0x000007c6 ] : ( ) => import ( './targets/esp32s2.js' ) ,
[ 0xfff0c101 ] : ( ) => import ( './targets/esp8266.js' ) ,
}
2021-04-18 17:46:46 +00:00
class ESPLoader {
2021-04-25 17:06:24 +00:00
ESP _RAM _BLOCK = 0x1800 ;
2021-05-03 04:00:25 +00:00
ESP _FLASH _BEGIN = 0x02 ;
ESP _FLASH _DATA = 0x03 ;
ESP _FLASH _END = 0x04 ;
2021-04-25 17:06:24 +00:00
ESP _MEM _BEGIN = 0x05 ;
ESP _MEM _END = 0x06 ;
2021-05-03 04:00:25 +00:00
ESP _MEM _DATA = 0x07 ;
ESP _WRITE _REG = 0x09 ;
2022-07-08 20:11:24 +00:00
ESP _READ _REG = 0x0A ;
ESP _SPI _ATTACH = 0x0D ;
ESP _CHANGE _BAUDRATE = 0x0F ;
2021-05-30 14:47:40 +00:00
ESP _FLASH _DEFL _BEGIN = 0x10 ;
ESP _FLASH _DEFL _DATA = 0x11 ;
ESP _FLASH _DEFL _END = 0x12 ;
ESP _SPI _FLASH _MD5 = 0x13 ;
2021-05-03 04:00:25 +00:00
2021-05-30 14:47:40 +00:00
// Only Stub supported commands
ESP _ERASE _FLASH = 0xD0 ;
ESP _ERASE _REGION = 0xD1 ;
2022-07-07 04:26:28 +00:00
ESP _RUN _USER _CODE = 0xD3 ;
2021-05-30 14:47:40 +00:00
ESP _IMAGE _MAGIC = 0xe9 ;
ESP _CHECKSUM _MAGIC = 0xef ;
2022-07-05 18:57:13 +00:00
// Response code(s) sent by ROM
ROM _INVALID _RECV _MSG = 0x05 // response if an invalid message is received
2021-05-03 04:00:25 +00:00
ERASE _REGION _TIMEOUT _PER _MB = 30000 ;
ERASE _WRITE _TIMEOUT _PER _MB = 40000 ;
2021-05-30 14:47:40 +00:00
MD5 _TIMEOUT _PER _MB = 8000 ;
2021-05-03 04:00:25 +00:00
CHIP _ERASE _TIMEOUT = 120000 ;
MAX _TIMEOUT = this . CHIP _ERASE _TIMEOUT * 2 ;
2021-05-30 14:47:40 +00:00
CHIP _DETECT _MAGIC _REG _ADDR = 0x40001000 ;
2021-05-03 04:00:25 +00:00
DETECTED _FLASH _SIZES = { 0x12 : '256KB' , 0x13 : '512KB' , 0x14 : '1MB' , 0x15 : '2MB' , 0x16 : '4MB' , 0x17 : '8MB' , 0x18 : '16MB' } ;
2021-04-25 17:06:24 +00:00
2021-07-18 16:13:30 +00:00
constructor ( transport , baudrate , terminal ) {
2021-04-18 17:46:46 +00:00
this . transport = transport ;
2021-07-18 16:13:30 +00:00
this . baudrate = baudrate ;
2021-04-18 17:46:46 +00:00
this . terminal = terminal ;
2021-05-03 04:00:25 +00:00
this . IS _STUB = false ;
2021-04-18 17:46:46 +00:00
this . chip = null ;
if ( terminal ) {
this . terminal . clear ( ) ;
}
2021-04-25 17:06:24 +00:00
2021-04-18 17:46:46 +00:00
this . log ( "esptool.js v0.1-dev" ) ;
this . log ( "Serial port " + this . transport . get _info ( ) ) ;
}
_sleep ( ms ) {
return new Promise ( resolve => setTimeout ( resolve , ms ) ) ;
}
log ( str ) {
2022-07-06 04:30:23 +00:00
if ( this . terminal ) {
2021-04-18 17:46:46 +00:00
this . terminal . writeln ( str ) ;
} else {
console . log ( str ) ;
}
}
write _char ( str ) {
2022-07-06 04:30:23 +00:00
if ( this . terminal ) {
2021-04-18 17:46:46 +00:00
this . terminal . write ( str ) ;
} else {
console . log ( str ) ;
}
}
_short _to _bytearray ( i ) {
return [ i & 0xff , ( i >> 8 ) & 0xff ] ;
}
_int _to _bytearray ( i ) {
return [ i & 0xff , ( i >> 8 ) & 0xff , ( i >> 16 ) & 0xff , ( i >> 24 ) & 0xff ] ;
}
2021-04-25 17:06:24 +00:00
2021-04-18 17:46:46 +00:00
_bytearray _to _short ( i , j ) {
return ( i | ( j >> 8 ) ) ;
}
_bytearray _to _int ( i , j , k , l ) {
return ( i | ( j << 8 ) | ( k << 16 ) | ( l << 24 ) ) ;
}
_appendBuffer ( buffer1 , buffer2 ) {
var tmp = new Uint8Array ( buffer1 . byteLength + buffer2 . byteLength ) ;
tmp . set ( new Uint8Array ( buffer1 ) , 0 ) ;
tmp . set ( new Uint8Array ( buffer2 ) , buffer1 . byteLength ) ;
return tmp . buffer ;
}
2021-04-25 17:06:24 +00:00
_appendArray ( arr1 , arr2 ) {
var c = new Uint8Array ( arr1 . length + arr2 . length ) ;
c . set ( arr1 , 0 ) ;
c . set ( arr2 , arr1 . length ) ;
return c ;
}
2021-05-30 14:47:40 +00:00
ui8ToBstr ( u8Array ) {
var i , len = u8Array . length , b _str = "" ;
for ( i = 0 ; i < len ; i ++ ) {
b _str += String . fromCharCode ( u8Array [ i ] ) ;
}
return b _str ;
}
bstrToUi8 ( bStr ) {
var i , len = bStr . length , u8 _array = new Uint8Array ( len ) ;
for ( var i = 0 ; i < len ; i ++ ) {
u8 _array [ i ] = bStr . charCodeAt ( i ) ;
}
return u8 _array ;
}
2022-07-05 18:57:13 +00:00
2021-04-18 17:46:46 +00:00
flush _input = async ( ) => {
try {
2022-07-05 18:57:13 +00:00
await this . transport . readRaw ( { timeout : 200 } ) ;
2021-04-18 17:46:46 +00:00
} catch ( e ) {
}
}
command = async ( { op = null , data = [ ] , chk = 0 , wait _response = true , timeout = 3000 } = { } ) => {
2021-05-30 14:47:40 +00:00
//console.log("command "+ op + " " + wait_response + " " + timeout);
2021-04-18 17:46:46 +00:00
if ( op != null ) {
var pkt = new Uint8Array ( 8 + data . length ) ;
pkt [ 0 ] = 0x00 ;
pkt [ 1 ] = op ;
pkt [ 2 ] = this . _short _to _bytearray ( data . length ) [ 0 ] ;
pkt [ 3 ] = this . _short _to _bytearray ( data . length ) [ 1 ] ;
pkt [ 4 ] = this . _int _to _bytearray ( chk ) [ 0 ] ;
pkt [ 5 ] = this . _int _to _bytearray ( chk ) [ 1 ] ;
pkt [ 6 ] = this . _int _to _bytearray ( chk ) [ 2 ] ;
pkt [ 7 ] = this . _int _to _bytearray ( chk ) [ 3 ] ;
var i ;
for ( i = 0 ; i < data . length ; i ++ ) {
pkt [ 8 + i ] = data [ i ] ;
}
2021-05-30 14:47:40 +00:00
//console.log("Command " + pkt);
2021-04-18 17:46:46 +00:00
await this . transport . write ( pkt ) ;
}
2021-04-25 17:06:24 +00:00
2021-04-18 17:46:46 +00:00
if ( wait _response ) {
2022-07-12 18:54:25 +00:00
// Check up-to next 100 packets for valid response packet
for ( let i = 0 ; i < 100 ; i ++ ) {
2021-04-25 17:06:24 +00:00
var p = await this . transport . read ( { timeout : timeout } ) ;
2021-05-30 14:47:40 +00:00
//console.log("Response " + p);
2021-04-18 17:46:46 +00:00
const resp = p [ 0 ] ;
const op _ret = p [ 1 ] ;
const len _ret = this . _bytearray _to _short ( p [ 2 ] , p [ 3 ] ) ;
const val = this . _bytearray _to _int ( p [ 4 ] , p [ 5 ] , p [ 6 ] , p [ 7 ] ) ;
2021-05-30 14:47:40 +00:00
//console.log("Resp "+resp + " " + op_ret + " " + len_ret + " " + val );
2021-04-18 17:46:46 +00:00
const data = p . slice ( 8 ) ;
2022-07-12 18:54:25 +00:00
if ( resp == 1 ) {
if ( op == null || op _ret == op ) {
return [ val , data ] ;
} else if ( data [ 0 ] != 0 && data [ 1 ] == this . ROM _INVALID _RECV _MSG ) {
await this . flush _input ( ) ;
throw new ESPError ( "unsupported command error" ) ;
2022-07-05 18:57:13 +00:00
}
2021-04-18 17:46:46 +00:00
}
}
2022-07-12 18:54:25 +00:00
throw new ESPError ( "invalid response" )
2021-04-18 17:46:46 +00:00
}
}
read _reg = async ( { addr , timeout = 3000 } = { } ) => {
var val , data ;
var pkt = this . _int _to _bytearray ( addr ) ;
2021-05-03 04:00:25 +00:00
val = await this . command ( { op : this . ESP _READ _REG , data : pkt , timeout : timeout } ) ;
2021-04-18 17:46:46 +00:00
return val [ 0 ] ;
}
2021-05-03 04:00:25 +00:00
write _reg = async ( { addr , value , mask = 0xFFFFFFFF , delay _us = 0 , delay _after _us = 0 } = { } ) => {
var pkt = this . _appendArray ( this . _int _to _bytearray ( addr ) , this . _int _to _bytearray ( value ) ) ;
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( mask ) ) ;
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( delay _us ) ) ;
if ( delay _after _us > 0 ) {
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( this . chip . UART _DATE _REG _ADDR ) ) ;
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( 0 ) ) ;
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( 0 ) ) ;
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( delay _after _us ) ) ;
}
await this . check _command ( { op _description : "write target memory" , op : this . ESP _WRITE _REG , data : pkt } ) ;
}
2021-04-18 17:46:46 +00:00
sync = async ( ) => {
console . log ( "Sync" ) ;
var cmd = new Uint8Array ( 36 ) ;
var i ;
cmd [ 0 ] = 0x07 ;
cmd [ 1 ] = 0x07 ;
cmd [ 2 ] = 0x12 ;
cmd [ 3 ] = 0x20 ;
for ( i = 0 ; i < 32 ; i ++ ) {
cmd [ 4 + i ] = 0x55 ;
}
2021-04-25 17:06:24 +00:00
2021-04-18 17:46:46 +00:00
try {
const resp = await this . command ( { op : 0x08 , data : cmd , timeout : 100 } ) ;
return resp ;
} catch ( e ) {
console . log ( "Sync err " + e ) ;
2022-07-08 17:54:19 +00:00
throw e ;
2021-04-18 17:46:46 +00:00
}
}
2021-04-25 17:06:24 +00:00
2021-04-18 17:46:46 +00:00
_connect _attempt = async ( { mode = 'default_reset' , esp32r0 _delay = false } = { } ) => {
2022-07-09 00:40:02 +00:00
console . log ( "_connect_attempt " + mode + " " + esp32r0 _delay ) ;
2021-04-18 17:46:46 +00:00
if ( mode !== 'no_reset' ) {
await this . transport . setDTR ( false ) ;
await this . transport . setRTS ( true ) ;
await this . _sleep ( 100 ) ;
if ( esp32r0 _delay ) {
//await this._sleep(1200);
await this . _sleep ( 2000 ) ;
}
await this . transport . setDTR ( true ) ;
await this . transport . setRTS ( false ) ;
if ( esp32r0 _delay ) {
//await this._sleep(400);
}
await this . _sleep ( 50 ) ;
await this . transport . setDTR ( false ) ;
}
var i = 0 ;
while ( 1 ) {
try {
2021-04-25 17:06:24 +00:00
const res = await this . transport . read ( { timeout : 1000 } ) ;
2021-04-18 17:46:46 +00:00
i += res . length ;
//console.log("Len = " + res.length);
//var str = new TextDecoder().decode(res);
//this.log(str);
} catch ( e ) {
2022-07-08 17:54:19 +00:00
if ( e instanceof TimeoutError ) {
2021-04-18 17:46:46 +00:00
break ;
}
}
await this . _sleep ( 50 ) ;
}
this . transport . slip _reader _enabled = true ;
var i = 7 ;
while ( i -- ) {
try {
var resp = await this . sync ( ) ;
return "success" ;
} catch ( error ) {
2022-07-08 17:54:19 +00:00
if ( error instanceof TimeoutError ) {
2021-04-18 17:46:46 +00:00
if ( esp32r0 _delay ) {
this . write _char ( '_' ) ;
} else {
this . write _char ( '.' ) ;
}
}
}
await this . _sleep ( 50 ) ;
}
2021-04-25 17:06:24 +00:00
return "error" ;
2021-04-18 17:46:46 +00:00
}
2021-04-25 17:06:24 +00:00
2021-04-18 17:46:46 +00:00
connect = async ( { mode = 'default_reset' , attempts = 7 , detecting = false } = { } ) => {
var i ;
var resp ;
2022-02-24 10:41:29 +00:00
this . chip = null ;
2021-04-18 17:46:46 +00:00
this . write _char ( 'Connecting...' ) ;
await this . transport . connect ( ) ;
for ( i = 0 ; i < attempts ; i ++ ) {
2022-07-09 00:40:02 +00:00
resp = await this . _connect _attempt ( { mode : mode , esp32r0 _delay : false } ) ;
2021-04-18 17:46:46 +00:00
if ( resp === "success" ) {
break ;
}
2022-07-09 00:40:02 +00:00
resp = await this . _connect _attempt ( { mode : mode , esp32r0 _delay : true } ) ;
2021-04-18 17:46:46 +00:00
if ( resp === "success" ) {
break ;
}
}
if ( resp !== "success" ) {
2022-07-08 17:54:19 +00:00
throw new ESPError ( "Failed to connect with the device" ) ;
2021-04-18 17:46:46 +00:00
}
2021-04-25 17:06:24 +00:00
this . write _char ( '\n' ) ;
this . write _char ( '\r' ) ;
2021-04-18 17:46:46 +00:00
await this . flush _input ( ) ;
if ( ! detecting ) {
2022-07-05 18:57:13 +00:00
var chip _magic _value = await this . read _reg ( { addr : 0x40001000 } ) >>> 0 ;
console . log ( "Chip Magic " + chip _magic _value . toString ( 16 ) ) ;
2022-07-08 06:48:22 +00:00
if ( chip _magic _value in MAGIC _TO _CHIP ) {
2022-07-08 07:01:19 +00:00
this . chip = ( await MAGIC _TO _CHIP [ chip _magic _value ] ( ) ) . default ;
} else {
2022-07-08 17:54:19 +00:00
throw new ESPError ( ` Unexpected CHIP magic value ${ chip _magic _value } . Failed to autodetect chip type. ` ) ;
2022-07-08 07:01:19 +00:00
}
2021-04-18 17:46:46 +00:00
}
}
2022-07-09 00:40:02 +00:00
detect _chip = async ( { mode = 'default_reset' } = { } ) => {
await this . connect ( { mode : mode } ) ;
2021-04-18 17:46:46 +00:00
this . write _char ( "Detecting chip type... " ) ;
if ( this . chip != null ) {
this . log ( this . chip . CHIP _NAME ) ;
}
}
2021-04-25 17:06:24 +00:00
check _command = async ( { op _description = "" , op = null , data = [ ] , chk = 0 , timeout = 3000 } = { } ) => {
2022-07-08 20:11:24 +00:00
console . log ( "check_command " + op _description ) ;
2021-04-25 17:06:24 +00:00
var resp = await this . command ( { op : op , data : data , chk : chk , timeout : timeout } ) ;
2021-05-30 14:47:40 +00:00
if ( resp [ 1 ] . length > 4 ) {
return resp [ 1 ] ;
} else {
return resp [ 0 ] ;
}
2021-04-25 17:06:24 +00:00
}
mem _begin = async ( size , blocks , blocksize , offset ) => {
/* XXX: Add check to ensure that STUB is not getting overwritten */
2022-07-08 20:11:24 +00:00
console . log ( "mem_begin " + size + " " + blocks + " " + blocksize + " " + offset . toString ( 16 ) ) ;
2021-04-25 17:06:24 +00:00
var pkt = this . _appendArray ( this . _int _to _bytearray ( size ) , this . _int _to _bytearray ( blocks ) ) ;
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( blocksize ) ) ;
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( offset ) ) ;
2022-07-08 20:11:24 +00:00
await this . check _command ( { op _description : "enter RAM download mode" , op : this . ESP _MEM _BEGIN , data : pkt } ) ;
2021-04-25 17:06:24 +00:00
}
checksum = function ( data ) {
var i ;
var chk = 0xEF ;
for ( i = 0 ; i < data . length ; i ++ ) {
chk ^= data [ i ] ;
}
return chk ;
}
mem _block = async ( buffer , seq ) => {
var pkt = this . _appendArray ( this . _int _to _bytearray ( buffer . length ) , this . _int _to _bytearray ( seq ) ) ;
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( 0 ) ) ;
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( 0 ) ) ;
pkt = this . _appendArray ( pkt , buffer ) ;
var checksum = this . checksum ( buffer ) ;
await this . check _command ( { op _description : "write to target RAM" , op : this . ESP _MEM _DATA , data : pkt , chk : checksum } ) ;
}
mem _finish = async ( entrypoint ) => {
var is _entry = ( entrypoint === 0 ) ? 1 : 0 ;
var pkt = this . _appendArray ( this . _int _to _bytearray ( is _entry ) , this . _int _to _bytearray ( entrypoint ) ) ;
await this . check _command ( { op _description : "leave RAM download mode" , op : this . ESP _MEM _END , data : pkt , timeout : 50 } ) ; // XXX: handle non-stub with diff timeout
}
2021-05-03 04:00:25 +00:00
flash _spi _attach = async ( hspi _arg ) => {
var pkt = this . _int _to _bytearray ( hspi _arg ) ;
await this . check _command ( { op _description : "configure SPI flash pins" , op : this . ESP _SPI _ATTACH , data : pkt } ) ;
}
timeout _per _mb = function ( seconds _per _mb , size _bytes ) {
var result = seconds _per _mb * ( size _bytes / 1000000 ) ;
if ( result < 3000 ) {
return 3000 ;
} else {
return result ;
}
}
flash _begin = async ( size , offset ) => {
2021-05-30 14:47:40 +00:00
var num _blocks = Math . floor ( ( size + this . FLASH _WRITE _SIZE - 1 ) / this . FLASH _WRITE _SIZE ) ;
2021-05-03 04:00:25 +00:00
var erase _size = this . chip . get _erase _size ( offset , size ) ;
var d = new Date ( ) ;
var t1 = d . getTime ( ) ;
var timeout = 3000 ;
if ( this . IS _STUB == false ) {
timeout = this . timeout _per _mb ( this . ERASE _REGION _TIMEOUT _PER _MB , size ) ;
}
2021-05-30 14:47:40 +00:00
console . log ( "flash begin " + erase _size + " " + num _blocks + " " + this . FLASH _WRITE _SIZE + " " + offset + " " + size ) ;
2021-05-03 04:00:25 +00:00
var pkt = this . _appendArray ( this . _int _to _bytearray ( erase _size ) , this . _int _to _bytearray ( num _blocks ) ) ;
2021-05-30 14:47:40 +00:00
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( this . FLASH _WRITE _SIZE ) ) ;
2021-05-03 04:00:25 +00:00
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( offset ) ) ;
if ( this . IS _STUB == false ) {
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( 0 ) ) ; // XXX: Support encrypted
}
await this . check _command ( { op _description : "enter Flash download mode" , op : this . ESP _FLASH _BEGIN , data : pkt , timeout : timeout } ) ;
var t2 = d . getTime ( ) ;
if ( size != 0 && this . IS _STUB == false ) {
this . log ( "Took " + ( ( t2 - t1 ) / 1000 ) + "." + ( ( t2 - t1 ) % 1000 ) + "s to erase flash block" ) ;
}
2022-07-08 20:11:24 +00:00
return num _blocks ;
2021-05-03 04:00:25 +00:00
}
2021-05-30 14:47:40 +00:00
flash _defl _begin = async ( size , compsize , offset ) => {
var num _blocks = Math . floor ( ( compsize + this . FLASH _WRITE _SIZE - 1 ) / this . FLASH _WRITE _SIZE ) ;
var erase _blocks = Math . floor ( ( size + this . FLASH _WRITE _SIZE - 1 ) / this . FLASH _WRITE _SIZE ) ;
var d = new Date ( ) ;
var t1 = d . getTime ( ) ;
let write _size , timeout ;
if ( this . IS _STUB ) {
write _size = size ;
timeout = 3000 ;
} else {
write _size = erase _blocks * this . FLASH _WRITE _SIZE ;
timeout = this . timeout _per _mb ( this . ERASE _REGION _TIMEOUT _PER _MB , write _size ) ;
}
this . log ( "Compressed " + size + " bytes to " + compsize + "..." ) ;
var pkt = this . _appendArray ( this . _int _to _bytearray ( write _size ) , this . _int _to _bytearray ( num _blocks ) ) ;
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( this . FLASH _WRITE _SIZE ) ) ;
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( offset ) ) ;
if ( ( this . chip . CHIP _NAME === "ESP32-S2" || this . chip . CHIP _NAME === "ESP32-S3" || this . chip . CHIP _NAME === "ESP32-C3" ) && ( this . IS _STUB === false ) ) {
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( 0 ) ) ;
}
await this . check _command ( { op _description : "enter compressed flash mode" , op : this . ESP _FLASH _DEFL _BEGIN , data : pkt , timeout : timeout } ) ;
var t2 = d . getTime ( ) ;
if ( size != 0 && this . IS _STUB === false ) {
this . log ( "Took " + ( ( t2 - t1 ) / 1000 ) + "." + ( ( t2 - t1 ) % 1000 ) + "s to erase flash block" ) ;
}
return num _blocks ;
}
2021-05-03 04:00:25 +00:00
flash _block = async ( data , seq , timeout ) => {
var pkt = this . _appendArray ( this . _int _to _bytearray ( data . length ) , this . _int _to _bytearray ( seq ) ) ;
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( 0 ) ) ;
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( 0 ) ) ;
pkt = this . _appendArray ( pkt , data ) ;
var checksum = this . checksum ( data ) ;
await this . check _command ( { op _description : "write to target Flash after seq " + seq , op : this . ESP _FLASH _DATA , data : pkt , chk : checksum , timeout : timeout } ) ;
}
2021-05-30 14:47:40 +00:00
flash _defl _block = async ( data , seq , timeout ) => {
var pkt = this . _appendArray ( this . _int _to _bytearray ( data . length ) , this . _int _to _bytearray ( seq ) ) ;
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( 0 ) ) ;
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( 0 ) ) ;
pkt = this . _appendArray ( pkt , data ) ;
var checksum = this . checksum ( data ) ;
console . log ( "flash_defl_block " + data [ 0 ] . toString ( 16 ) , + " " + data [ 1 ] . toString ( 16 ) ) ;
await this . check _command ( { op _description : "write compressed data to flash after seq " + seq , op : this . ESP _FLASH _DEFL _DATA , data : pkt , chk : checksum , timeout : timeout } ) ;
}
2021-05-03 04:00:25 +00:00
flash _finish = async ( { reboot = false } = { } ) => {
var val = reboot ? 0 : 1 ;
var pkt = this . _int _to _bytearray ( val ) ;
await this . check _command ( { op _description : "leave Flash mode" , op : this . ESP _FLASH _END , data : pkt } ) ;
}
2021-05-30 14:47:40 +00:00
flash _defl _finish = async ( { reboot = false } = { } ) => {
var val = reboot ? 0 : 1 ;
var pkt = this . _int _to _bytearray ( val ) ;
await this . check _command ( { op _description : "leave compressed flash mode" , op : this . ESP _FLASH _DEFL _END , data : pkt } ) ;
}
2021-05-03 04:00:25 +00:00
run _spiflash _command = async ( spiflash _command , data , read _bits ) => {
// SPI_USR register flags
var SPI _USR _COMMAND = ( 1 << 31 ) ;
var SPI _USR _MISO = ( 1 << 28 ) ;
var SPI _USR _MOSI = ( 1 << 27 ) ;
// SPI registers, base address differs ESP32* vs 8266
var base = this . chip . SPI _REG _BASE ;
var SPI _CMD _REG = base + 0x00 ;
var SPI _USR _REG = base + this . chip . SPI _USR _OFFS ;
var SPI _USR1 _REG = base + this . chip . SPI _USR1 _OFFS ;
var SPI _USR2 _REG = base + this . chip . SPI _USR2 _OFFS ;
var SPI _W0 _REG = base + this . chip . SPI _W0 _OFFS ;
var set _data _lengths ;
if ( this . chip . SPI _MOSI _DLEN _OFFS != null ) {
set _data _lengths = async ( mosi _bits , miso _bits ) => {
var SPI _MOSI _DLEN _REG = base + this . chip . SPI _MOSI _DLEN _OFFS ;
var SPI _MISO _DLEN _REG = base + this . chip . SPI _MISO _DLEN _OFFS ;
if ( mosi _bits > 0 ) {
await this . write _reg ( { addr : SPI _MOSI _DLEN _REG , value : ( mosi _bits - 1 ) } ) ;
}
if ( miso _bits > 0 ) {
await this . write _reg ( { addr : SPI _MISO _DLEN _REG , value : ( miso _bits - 1 ) } ) ;
}
} ;
} else {
set _data _lengths = async ( mosi _bits , miso _bits ) => {
var SPI _DATA _LEN _REG = SPI _USR1 _REG ;
var SPI _MOSI _BITLEN _S = 17 ;
var SPI _MISO _BITLEN _S = 8 ;
2022-07-08 20:11:24 +00:00
var mosi _mask = ( mosi _bits === 0 ) ? 0 : ( mosi _bits - 1 ) ;
var miso _mask = ( miso _bits === 0 ) ? 0 : ( miso _bits - 1 ) ;
2021-05-03 04:00:25 +00:00
var val = ( miso _mask << SPI _MISO _BITLEN _S ) | ( mosi _mask << SPI _MOSI _BITLEN _S ) ;
await this . write _reg ( { addr : SPI _DATA _LEN _REG , value : val } ) ;
} ;
}
var SPI _CMD _USR = ( 1 << 18 ) ;
var SPI _USR2 _COMMAND _LEN _SHIFT = 28 ;
if ( read _bits > 32 ) {
2022-07-08 17:54:19 +00:00
throw new ESPError ( "Reading more than 32 bits back from a SPI flash operation is unsupported" ) ;
2021-05-03 04:00:25 +00:00
}
if ( data . length > 64 ) {
2022-07-08 17:54:19 +00:00
throw new ESPError ( "Writing more than 64 bytes of data with one SPI command is unsupported" ) ;
2021-05-03 04:00:25 +00:00
}
var data _bits = data . length * 8 ;
var old _spi _usr = await this . read _reg ( { addr : SPI _USR _REG } ) ;
var old _spi _usr2 = await this . read _reg ( { addr : SPI _USR2 _REG } ) ;
var flags = SPI _USR _COMMAND ;
var i ;
if ( read _bits > 0 ) {
flags |= SPI _USR _MISO ;
}
if ( data _bits > 0 ) {
flags |= SPI _USR _MOSI ;
}
await set _data _lengths ( data _bits , read _bits ) ;
await this . write _reg ( { addr : SPI _USR _REG , value : flags } ) ;
var val = ( 7 << SPI _USR2 _COMMAND _LEN _SHIFT ) | spiflash _command ;
await this . write _reg ( { addr : SPI _USR2 _REG , value : val } ) ;
if ( data _bits == 0 ) {
await this . write _reg ( { addr : SPI _W0 _REG , value : 0 } ) ;
} else {
if ( data . length % 4 != 0 ) {
var padding = new Uint8Array ( data . length % 4 ) ;
data = this . _appendArray ( data , padding ) ;
}
var next _reg = SPI _W0 _REG ;
for ( i = 0 ; i < data . length - 4 ; i += 4 ) {
val = this . _bytearray _to _int ( data [ i ] , data [ i + 1 ] , data [ i + 2 ] , data [ i + 3 ] ) ;
await this . write _reg ( { addr : next _reg , value : val } ) ;
next _reg += 4 ;
}
}
await this . write _reg ( { addr : SPI _CMD _REG , value : SPI _CMD _USR } ) ;
for ( i = 0 ; i < 10 ; i ++ ) {
val = await this . read _reg ( { addr : SPI _CMD _REG } ) & SPI _CMD _USR ;
if ( val == 0 ) {
break ;
}
}
if ( i === 10 ) {
2022-07-08 17:54:19 +00:00
throw new ESPError ( "SPI command did not complete in time" ) ;
2021-05-03 04:00:25 +00:00
}
var stat = await this . read _reg ( { addr : SPI _W0 _REG } ) ;
await this . write _reg ( { addr : SPI _USR _REG , value : old _spi _usr } ) ;
await this . write _reg ( { addr : SPI _USR2 _REG , value : old _spi _usr2 } ) ;
return stat ;
}
read _flash _id = async ( ) => {
var SPIFLASH _RDID = 0x9F ;
var pkt = new Uint8Array ( 0 ) ;
return await this . run _spiflash _command ( SPIFLASH _RDID , pkt , 24 ) ;
}
2021-05-30 14:47:40 +00:00
erase _flash = async ( ) => {
this . log ( "Erasing flash (this may take a while)..." ) ;
var d = new Date ( ) ;
let t1 = d . getTime ( ) ;
let ret = await this . check _command ( { op _description : "erase flash" , op : this . ESP _ERASE _FLASH , timeout : this . CHIP _ERASE _TIMEOUT } ) ;
d = new Date ( ) ;
let t2 = d . getTime ( ) ;
this . log ( "Chip erase completed successfully in " + ( t2 - t1 ) / 1000 + "s" ) ;
return ret ;
}
toHex ( buffer ) {
return Array . prototype . map . call ( buffer , x => ( '00' + x . toString ( 16 ) ) . slice ( - 2 ) ) . join ( '' ) ;
}
flash _md5sum = async ( addr , size ) => {
let timeout = this . timeout _per _mb ( this . MD5 _TIMEOUT _PER _MB , size ) ;
var pkt = this . _appendArray ( this . _int _to _bytearray ( addr ) , this . _int _to _bytearray ( size ) ) ;
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( 0 ) ) ;
pkt = this . _appendArray ( pkt , this . _int _to _bytearray ( 0 ) ) ;
let res = await this . check _command ( { op _description : "calculate md5sum" , op : this . ESP _SPI _FLASH _MD5 , data : pkt , timeout : timeout } ) ;
if ( res . length > 16 ) {
res = res . slice ( 0 , 16 ) ;
}
let strmd5 = this . toHex ( res ) ;
return strmd5 ;
2021-05-03 04:00:25 +00:00
}
2021-04-25 17:06:24 +00:00
run _stub = async ( ) => {
this . log ( "Uploading stub..." ) ;
var decoded = atob ( this . chip . ROM _TEXT ) ;
var chardata = decoded . split ( '' ) . map ( function ( x ) { return x . charCodeAt ( 0 ) ; } ) ;
var bindata = new Uint8Array ( chardata ) ;
var text = pako . inflate ( bindata ) ;
decoded = atob ( this . chip . ROM _DATA ) ;
chardata = decoded . split ( '' ) . map ( function ( x ) { return x . charCodeAt ( 0 ) ; } ) ;
var data = new Uint8Array ( chardata ) ;
var blocks = Math . floor ( ( text . length + this . ESP _RAM _BLOCK - 1 ) / this . ESP _RAM _BLOCK ) ;
var i ;
await this . mem _begin ( text . length , blocks , this . ESP _RAM _BLOCK , this . chip . TEXT _START ) ;
for ( i = 0 ; i < blocks ; i ++ ) {
var from _offs = i * this . ESP _RAM _BLOCK ;
var to _offs = from _offs + this . ESP _RAM _BLOCK ;
await this . mem _block ( text . slice ( from _offs , to _offs ) , i ) ;
}
blocks = Math . floor ( ( data . length + this . ESP _RAM _BLOCK - 1 ) / this . ESP _RAM _BLOCK ) ;
await this . mem _begin ( data . length , blocks , this . ESP _RAM _BLOCK , this . chip . DATA _START ) ;
for ( i = 0 ; i < blocks ; i ++ ) {
var from _offs = i * this . ESP _RAM _BLOCK ;
var to _offs = from _offs + this . ESP _RAM _BLOCK ;
await this . mem _block ( data . slice ( from _offs , to _offs ) , i ) ;
}
this . log ( "Running stub..." ) ;
await this . mem _finish ( this . chip . ENTRY ) ;
2022-07-12 18:54:25 +00:00
// Check up-to next 100 packets to see if stub is running
for ( let i = 0 ; i < 100 ; i ++ ) {
const res = await this . transport . read ( { timeout : 1000 , min _data : 6 } ) ;
if ( res [ 0 ] === 79 && res [ 1 ] === 72 && res [ 2 ] === 65 && res [ 3 ] === 73 ) {
this . log ( "Stub running..." ) ;
this . IS _STUB = true ;
this . FLASH _WRITE _SIZE = 0x4000 ;
return this . chip ;
2022-07-05 18:57:13 +00:00
}
2021-04-25 17:06:24 +00:00
}
2022-07-12 18:54:25 +00:00
throw new ESPError ( "Failed to start stub. Unexpected response" ) ;
2021-04-25 17:06:24 +00:00
}
2021-07-18 16:13:30 +00:00
change _baud = async ( ) => {
this . log ( "Changing baudrate to " + this . baudrate ) ;
let second _arg = this . IS _STUB ? this . transport . baudrate : 0 ;
let pkt = this . _appendArray ( this . _int _to _bytearray ( this . baudrate ) , this . _int _to _bytearray ( second _arg ) ) ;
let resp = await this . command ( { op : this . ESP _CHANGE _BAUDRATE , data : pkt } ) ;
this . log ( "Changed" ) ;
await this . transport . disconnect ( ) ;
await this . _sleep ( 50 ) ;
await this . transport . connect ( { baud : this . baudrate } ) ;
try {
await this . transport . rawRead ( { timeout : 500 } ) ;
} catch ( e ) {
}
}
2022-07-09 00:40:02 +00:00
main _fn = async ( { mode = 'default_reset' } = { } ) => {
2022-07-11 15:10:30 +00:00
await this . detect _chip ( { mode } ) ;
2021-04-18 17:46:46 +00:00
var chip = await this . chip . get _chip _description ( this ) ;
this . log ( "Chip is " + chip ) ;
this . log ( "Features: " + await this . chip . get _chip _features ( this ) ) ;
this . log ( "Crystal is " + await this . chip . get _crystal _freq ( this ) + "MHz" ) ;
this . log ( "MAC: " + await this . chip . read _mac ( this ) ) ;
2021-04-25 17:06:24 +00:00
await this . chip . read _mac ( this ) ;
2022-02-11 09:43:59 +00:00
if ( typeof ( this . chip . _post _connect ) != 'undefined' ) {
await this . chip . _post _connect ( this ) ;
}
2021-04-25 17:06:24 +00:00
await this . run _stub ( ) ;
2021-07-18 16:13:30 +00:00
await this . change _baud ( ) ;
2021-08-04 14:15:21 +00:00
return chip ;
2021-04-18 17:46:46 +00:00
}
2021-05-03 04:00:25 +00:00
2021-05-30 14:47:40 +00:00
flash _size _bytes = function ( flash _size ) {
let flash _size _b = - 1 ;
if ( flash _size . indexOf ( "KB" ) !== - 1 ) {
flash _size _b = parseInt ( flash _size . slice ( 0 , flash _size . indexOf ( "KB" ) ) ) * 1024 ;
} else if ( flash _size . indexOf ( "MB" ) !== - 1 ) {
flash _size _b = parseInt ( flash _size . slice ( 0 , flash _size . indexOf ( "MB" ) ) ) * 1024 * 1024 ;
}
return flash _size _b ;
}
parse _flash _size _arg = function ( flsz ) {
if ( typeof this . chip . FLASH _SIZES [ flsz ] === 'undefined' ) {
2022-07-08 17:54:19 +00:00
throw new ESPError ( "Flash size " + flsz + " is not supported by this chip type. Supported sizes: " + this . chip . FLASH _SIZES ) ;
2021-05-30 14:47:40 +00:00
}
return this . chip . FLASH _SIZES [ flsz ] ;
}
_update _image _flash _params = function ( image , address , flash _size , flash _mode , flash _freq ) {
console . log ( "_update_image_flash_params " + flash _size + " " + flash _mode + " " + flash _freq ) ;
if ( image . length < 8 ) {
return image ;
}
if ( address != this . chip . BOOTLOADER _FLASH _OFFSET ) {
return image ;
}
if ( flash _size === 'keep' && flash _mode === 'keep' && flash _freq === 'keep' ) {
console . log ( "Not changing the image" ) ;
return image ;
}
let magic = image [ 0 ] ;
let a _flash _mode = image [ 2 ] ;
let flash _size _freq = image [ 3 ] ;
if ( magic !== this . ESP _IMAGE _MAGIC ) {
this . log ( "Warning: Image file at 0x" + address . toString ( 16 ) + " doesn't look like an image file, so not changing any flash settings." ) ;
return image ;
}
/* XXX: Yet to implement actual image verification */
if ( flash _mode !== 'keep' ) {
let flash _modes = { 'qio' : 0 , 'qout' : 1 , 'dio' : 2 , 'dout' : 3 } ;
a _flash _mode = flash _modes [ flash _mode ] ;
}
a _flash _freq = flash _size _freq & 0x0F ;
if ( flash _freq !== 'keep' ) {
let flash _freqs = { '40m' : 0 , '26m' : 1 , '20m' : 2 , '80m' : 0xf } ;
a _flash _freq = flash _freqs [ flash _freq ] ;
}
a _flash _size = flash _size _freq & 0xF0 ;
if ( flash _size !== 'keep' ) {
a _flash _size = this . parse _flash _size _arg ( flash _size ) ;
}
var flash _params = ( a _flash _mode << 8 ) | ( a _flash _freq + a _flash _size ) ;
this . log ( "Flash params set to " + flash _params . toString ( 16 ) ) ;
if ( image [ 2 ] !== ( a _flash _mode << 8 ) ) {
image [ 2 ] = ( a _flash _mode << 8 ) ;
}
if ( image [ 3 ] !== ( a _flash _freq + a _flash _size ) ) {
image [ 3 ] = ( a _flash _freq + a _flash _size ) ;
}
return image ;
}
2022-07-08 17:17:17 +00:00
write _flash = async ( {
fileArray = [ ] ,
flash _size = 'keep' ,
flash _mode = 'keep' ,
flash _freq = 'keep' ,
erase _all = false ,
compress = true ,
/* function(fileIndex, written, total) */
reportProgress = undefined ,
2022-07-08 16:08:00 +00:00
/* function(image: string) => string */
calculateMD5Hash = undefined
} ) => {
2021-05-03 04:00:25 +00:00
console . log ( "EspLoader program" ) ;
2021-05-30 14:47:40 +00:00
if ( flash _size !== 'keep' ) {
let flash _end = this . flash _size _bytes ( flash _size ) ;
for ( var i = 0 ; i < fileArray . length ; i ++ ) {
if ( ( fileArray [ i ] . data . length + fileArray [ i ] . address ) > flash _end ) {
2022-07-08 17:54:19 +00:00
throw new ESPError ( ` File ${ i + 1 } doesn't fit in the available flash ` ) ;
2021-05-30 14:47:40 +00:00
}
}
}
if ( this . IS _STUB === true && erase _all === true ) {
2022-07-16 23:52:14 +00:00
await this . erase _flash ( ) ;
2021-05-30 14:47:40 +00:00
}
let image , address ;
for ( var i = 0 ; i < fileArray . length ; i ++ ) {
console . log ( "Data Length " + fileArray [ i ] . data . length ) ;
2022-07-05 18:57:13 +00:00
image = fileArray [ i ] . data + '\xff\xff\xff\xff' . substring ( 0 , 4 - fileArray [ i ] . data . length % 4 ) ;
2021-05-30 14:47:40 +00:00
address = fileArray [ i ] . address ;
console . log ( "Image Length " + image . length ) ;
if ( image . length === 0 ) {
this . log ( "Warning: File is empty" ) ;
continue ;
}
image = this . _update _image _flash _params ( image , address , flash _size , flash _mode , flash _freq ) ;
2022-07-08 16:08:00 +00:00
let calcmd5 ;
if ( calculateMD5Hash ) {
calcmd5 = calculateMD5Hash ( image ) ;
console . log ( "Image MD5 " + calcmd5 ) ;
}
2021-05-30 14:47:40 +00:00
let uncsize = image . length ;
let blocks ;
if ( compress ) {
let uncimage = this . bstrToUi8 ( image ) ;
image = pako . deflate ( uncimage , { level : 9 } ) ;
console . log ( "Compressed image " ) ;
console . log ( image ) ;
blocks = await this . flash _defl _begin ( uncsize , image . length , address ) ;
} else {
blocks = await this . flash _begin ( uncsize , address ) ;
}
let seq = 0 ;
let bytes _sent = 0 ;
let bytes _written = 0 ;
2022-07-08 17:17:17 +00:00
const totalBytes = image . length ;
if ( reportProgress ) reportProgress ( i , 0 , totalBytes ) ;
2021-05-30 14:47:40 +00:00
var d = new Date ( ) ;
let t1 = d . getTime ( ) ;
let timeout = 5000 ;
while ( image . length > 0 ) {
console . log ( "Write loop " + address + " " + seq + " " + blocks ) ;
this . log ( "Writing at 0x" + ( address + ( seq * this . FLASH _WRITE _SIZE ) ) . toString ( 16 ) + "... (" + Math . floor ( 100 * ( seq + 1 ) / blocks ) + "%)" ) ;
let block = image . slice ( 0 , this . FLASH _WRITE _SIZE ) ;
if ( compress ) {
/ *
let block _uncompressed = pako . inflate ( block ) . length ;
//let len_uncompressed = block_uncompressed.length;
bytes _written += block _uncompressed ;
if ( this . timeout _per _mb ( this . ERASE _WRITE _TIMEOUT _PER _MB , block _uncompressed ) > 3000 ) {
block _timeout = this . timeout _per _mb ( this . ERASE _WRITE _TIMEOUT _PER _MB , block _uncompressed ) ;
} else {
block _timeout = 3000 ;
} * / / / XXX : Partial block inflate seems to be unsupported in Pako . Hardcoding timeout
let block _timeout = 5000 ;
if ( this . IS _STUB === false ) {
timeout = block _timeout ;
}
await this . flash _defl _block ( block , seq , timeout ) ;
if ( this . IS _STUB ) {
timeout = block _timeout ;
}
} else {
2022-07-08 17:54:19 +00:00
throw new ESPError ( "Yet to handle Non Compressed writes" ) ;
2021-05-30 14:47:40 +00:00
}
bytes _sent += block . length ;
image = image . slice ( this . FLASH _WRITE _SIZE , image . length ) ;
seq ++ ;
2022-07-08 17:17:17 +00:00
if ( reportProgress ) reportProgress ( i , bytes _sent , totalBytes ) ;
2021-05-30 14:47:40 +00:00
}
if ( this . IS _STUB ) {
await this . read _reg ( { addr : this . CHIP _DETECT _MAGIC _REG _ADDR , timeout : timeout } ) ;
}
d = new Date ( ) ;
let t = d . getTime ( ) - t1 ;
if ( compress ) {
this . log ( "Wrote " + uncsize + " bytes (" + bytes _sent + " compressed) at 0x" + address . toString ( 16 ) + " in " + ( t / 1000 ) + " seconds." ) ;
}
2022-07-08 16:08:00 +00:00
if ( calculateMD5Hash ) {
let res = await this . flash _md5sum ( address , uncsize ) ;
if ( new String ( res ) . valueOf ( ) != new String ( calcmd5 ) . valueOf ( ) ) {
this . log ( "File md5: " + calcmd5 ) ;
this . log ( "Flash md5: " + res ) ;
throw new ESPError ( "MD5 of file does not match data in flash!" )
} else {
this . log ( "Hash of data verified." ) ;
}
2021-05-30 14:47:40 +00:00
}
}
this . log ( "Leaving..." ) ;
if ( this . IS _STUB ) {
await this . flash _begin ( 0 , 0 ) ;
if ( compress ) {
await this . flash _defl _finish ( ) ;
} else {
await this . flash _finish ( ) ;
}
}
2021-05-03 04:00:25 +00:00
}
flash _id = async ( ) => {
console . log ( "flash_id" ) ;
var flashid = await this . read _flash _id ( ) ;
this . log ( "Manufacturer: " + ( flashid & 0xff ) . toString ( 16 ) ) ;
var flid _lowbyte = ( flashid >> 16 ) & 0xff ;
this . log ( "Device: " + ( ( flashid >> 8 ) & 0xff ) . toString ( 16 ) + flid _lowbyte . toString ( 16 ) ) ;
this . log ( "Detected flash size: " + this . DETECTED _FLASH _SIZES [ flid _lowbyte ] ) ;
}
2022-07-07 04:26:28 +00:00
2022-07-07 04:29:24 +00:00
hard _reset = async ( ) => {
this . transport . setRTS ( true ) ; // EN->LOW
await this . _sleep ( 100 ) ;
this . transport . setRTS ( false ) ;
}
2022-07-07 04:26:28 +00:00
soft _reset = async ( ) => {
if ( ! this . IS _STUB ) {
// 'run user code' is as close to a soft reset as we can do
this . flash _begin ( 0 , 0 ) ;
this . flash _finish ( false ) ;
2022-07-11 04:42:02 +00:00
} else if ( this . chip . CHIP _NAME != "ESP8266" ) {
2022-07-12 07:15:40 +00:00
throw new ESPError ( "Soft resetting is currently only supported on ESP8266" ) ;
2022-07-07 04:26:28 +00:00
} else {
2022-07-11 04:42:02 +00:00
// running user code from stub loader requires some hacks
// in the stub loader
this . command ( { op : this . ESP _RUN _USER _CODE , wait _response : false } ) ;
2022-07-07 04:26:28 +00:00
}
}
2021-04-18 17:46:46 +00:00
}
export { ESPLoader } ;