tasmota-webserial-esptool 7.0.1 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/css/style.css +95 -0
- package/dist/const.js +1 -25
- package/dist/esp_loader.d.ts +4 -2
- package/dist/esp_loader.js +164 -176
- package/dist/struct.js +9 -23
- package/dist/util.js +4 -4
- package/dist/web/index.js +1 -1
- package/eslint.config.js +22 -0
- package/index.html +12 -1
- package/js/modules/esptool.js +1 -1
- package/js/script.js +65 -2
- package/package.json +14 -5
- package/src/const.ts +3 -25
- package/src/esp_loader.ts +184 -204
- package/src/struct.ts +18 -25
- package/src/util.ts +4 -4
- package/.devcontainer/devcontainer.json +0 -22
- package/.github/dependabot.yml +0 -10
- package/.github/workflows/build_upload.yml +0 -75
- package/.github/workflows/ci.yml +0 -30
- package/.prettierignore +0 -1
- package/CHANGELOG_CHIP_VARIANT.md +0 -169
- package/CHIP_VARIANT_SUPPORT.md +0 -184
- package/READ_FLASH_FEATURE.md +0 -130
- package/rollup.config.js +0 -27
- package/tsconfig.json +0 -20
package/dist/esp_loader.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { CHIP_FAMILY_ESP32, CHIP_FAMILY_ESP32S2, CHIP_FAMILY_ESP32S3, CHIP_FAMILY_ESP32C2, CHIP_FAMILY_ESP32C3, CHIP_FAMILY_ESP32C5, CHIP_FAMILY_ESP32C6, CHIP_FAMILY_ESP32C61, CHIP_FAMILY_ESP32H2, CHIP_FAMILY_ESP32H4, CHIP_FAMILY_ESP32H21, CHIP_FAMILY_ESP32P4, CHIP_FAMILY_ESP32S31, CHIP_FAMILY_ESP8266, MAX_TIMEOUT, DEFAULT_TIMEOUT, ERASE_REGION_TIMEOUT_PER_MB, ESP_CHANGE_BAUDRATE, ESP_CHECKSUM_MAGIC, ESP_FLASH_BEGIN, ESP_FLASH_DATA, ESP_FLASH_END, ESP_MEM_BEGIN, ESP_MEM_DATA, ESP_MEM_END, ESP_READ_REG, ESP_WRITE_REG, ESP_SPI_ATTACH, ESP_SYNC, ESP_GET_SECURITY_INFO, FLASH_SECTOR_SIZE, FLASH_WRITE_SIZE, STUB_FLASH_WRITE_SIZE, MEM_END_ROM_TIMEOUT, ROM_INVALID_RECV_MSG, SYNC_PACKET, SYNC_TIMEOUT, USB_RAM_BLOCK, ESP_ERASE_FLASH, ESP_READ_FLASH, CHIP_ERASE_TIMEOUT, FLASH_READ_TIMEOUT, timeoutPerMb, ESP_ROM_BAUD, USB_JTAG_SERIAL_PID, ESP_FLASH_DEFL_BEGIN, ESP_FLASH_DEFL_DATA, ESP_FLASH_DEFL_END, getSpiFlashAddresses, DETECTED_FLASH_SIZES, CHIP_DETECT_MAGIC_REG_ADDR, CHIP_DETECT_MAGIC_VALUES, CHIP_ID_TO_INFO, ESP32P4_EFUSE_BLOCK1_ADDR, SlipReadError, } from "./const";
|
|
3
3
|
import { getStubCode } from "./stubs";
|
|
4
4
|
import { hexFormatter, sleep, slipEncode, toHex } from "./util";
|
|
5
|
-
// @ts-
|
|
5
|
+
// @ts-expect-error pako ESM module doesn't have proper type definitions
|
|
6
6
|
import { deflate } from "pako/dist/pako.esm.mjs";
|
|
7
7
|
import { pack, unpack } from "./struct";
|
|
8
8
|
export class ESPLoader extends EventTarget {
|
|
@@ -21,6 +21,8 @@ export class ESPLoader extends EventTarget {
|
|
|
21
21
|
this.connected = true;
|
|
22
22
|
this.flashSize = null;
|
|
23
23
|
this._currentBaudRate = ESP_ROM_BAUD;
|
|
24
|
+
this._isESP32S2NativeUSB = false;
|
|
25
|
+
this._initializationSucceeded = false;
|
|
24
26
|
this.state_DTR = false;
|
|
25
27
|
}
|
|
26
28
|
get _inputBuffer() {
|
|
@@ -44,8 +46,13 @@ export class ESPLoader extends EventTarget {
|
|
|
44
46
|
const chips = {
|
|
45
47
|
0x1a86: {
|
|
46
48
|
// QinHeng Electronics
|
|
49
|
+
0x7522: { name: "CH340", maxBaudrate: 460800 },
|
|
47
50
|
0x7523: { name: "CH340", maxBaudrate: 460800 },
|
|
51
|
+
0x7584: { name: "CH340", maxBaudrate: 460800 },
|
|
52
|
+
0x5523: { name: "CH341", maxBaudrate: 2000000 },
|
|
53
|
+
0x55d3: { name: "CH343", maxBaudrate: 6000000 },
|
|
48
54
|
0x55d4: { name: "CH9102", maxBaudrate: 6000000 },
|
|
55
|
+
0x55d8: { name: "CH9101", maxBaudrate: 3000000 },
|
|
49
56
|
},
|
|
50
57
|
0x10c4: {
|
|
51
58
|
// Silicon Labs
|
|
@@ -92,6 +99,10 @@ export class ESPLoader extends EventTarget {
|
|
|
92
99
|
this._maxUSBSerialBaudrate = chipInfo.maxBaudrate;
|
|
93
100
|
this.logger.log(`Max baudrate: ${chipInfo.maxBaudrate}`);
|
|
94
101
|
}
|
|
102
|
+
// Detect ESP32-S2 Native USB
|
|
103
|
+
if (portInfo.usbVendorId === 0x303a && portInfo.usbProductId === 0x2) {
|
|
104
|
+
this._isESP32S2NativeUSB = true;
|
|
105
|
+
}
|
|
95
106
|
}
|
|
96
107
|
// Don't await this promise so it doesn't block rest of method.
|
|
97
108
|
this.readLoop();
|
|
@@ -102,13 +113,15 @@ export class ESPLoader extends EventTarget {
|
|
|
102
113
|
// Detect chip type
|
|
103
114
|
await this.detectChip();
|
|
104
115
|
// Read the OTP data for this chip and store into this.efuses array
|
|
105
|
-
|
|
106
|
-
|
|
116
|
+
const FlAddr = getSpiFlashAddresses(this.getChipFamily());
|
|
117
|
+
const AddrMAC = FlAddr.macFuse;
|
|
107
118
|
for (let i = 0; i < 4; i++) {
|
|
108
119
|
this._efuses[i] = await this.readRegister(AddrMAC + 4 * i);
|
|
109
120
|
}
|
|
110
121
|
this.logger.log(`Chip type ${this.chipName}`);
|
|
111
122
|
this.logger.debug(`Bootloader flash offset: 0x${FlAddr.flashOffs.toString(16)}`);
|
|
123
|
+
// Mark initialization as successful
|
|
124
|
+
this._initializationSucceeded = true;
|
|
112
125
|
}
|
|
113
126
|
/**
|
|
114
127
|
* Detect chip type using GET_SECURITY_INFO (for newer chips) or magic value (for older chips)
|
|
@@ -140,9 +153,9 @@ export class ESPLoader extends EventTarget {
|
|
|
140
153
|
}
|
|
141
154
|
this.logger.debug(`Unknown IMAGE_CHIP_ID: ${chipId}, falling back to magic value detection`);
|
|
142
155
|
}
|
|
143
|
-
catch (
|
|
156
|
+
catch (error) {
|
|
144
157
|
// GET_SECURITY_INFO not supported, fall back to magic value detection
|
|
145
|
-
this.logger.debug(`GET_SECURITY_INFO failed, using magic value detection: ${
|
|
158
|
+
this.logger.debug(`GET_SECURITY_INFO failed, using magic value detection: ${error}`);
|
|
146
159
|
// Clear input buffer and re-sync to recover from failed command
|
|
147
160
|
this._inputBuffer.length = 0;
|
|
148
161
|
await sleep(SYNC_TIMEOUT);
|
|
@@ -154,15 +167,14 @@ export class ESPLoader extends EventTarget {
|
|
|
154
167
|
this.logger.debug(`Re-sync after GET_SECURITY_INFO failure: ${syncErr}`);
|
|
155
168
|
}
|
|
156
169
|
}
|
|
157
|
-
// Fallback: Use magic value detection for ESP8266, ESP32, ESP32-S2
|
|
158
|
-
|
|
159
|
-
|
|
170
|
+
// Fallback: Use magic value detection for ESP8266, ESP32, ESP32-S2
|
|
171
|
+
const chipMagicValue = await this.readRegister(CHIP_DETECT_MAGIC_REG_ADDR);
|
|
172
|
+
const chip = CHIP_DETECT_MAGIC_VALUES[chipMagicValue >>> 0];
|
|
160
173
|
if (chip === undefined) {
|
|
161
174
|
throw new Error(`Unknown Chip: Hex: ${toHex(chipMagicValue >>> 0, 8).toLowerCase()} Number: ${chipMagicValue}`);
|
|
162
175
|
}
|
|
163
176
|
this.chipName = chip.name;
|
|
164
177
|
this.chipFamily = chip.family;
|
|
165
|
-
// For ESP32-P4 detected via magic value (old revisions), set variant
|
|
166
178
|
if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
167
179
|
this.chipRevision = await this.getChipRevision();
|
|
168
180
|
this.logger.debug(`ESP32-P4 revision: ${this.chipRevision}`);
|
|
@@ -197,7 +209,7 @@ export class ESPLoader extends EventTarget {
|
|
|
197
209
|
* Get security info including chip ID (ESP32-C3 and later)
|
|
198
210
|
*/
|
|
199
211
|
async getSecurityInfo() {
|
|
200
|
-
const [
|
|
212
|
+
const [, responseData] = await this.checkCommand(ESP_GET_SECURITY_INFO, [], 0);
|
|
201
213
|
// Some chips/ROM versions return empty response or don't support this command
|
|
202
214
|
if (responseData.length === 0) {
|
|
203
215
|
throw new Error(`GET_SECURITY_INFO not supported or returned empty response`);
|
|
@@ -232,10 +244,12 @@ export class ESPLoader extends EventTarget {
|
|
|
232
244
|
}
|
|
233
245
|
this._reader = this.port.readable.getReader();
|
|
234
246
|
try {
|
|
235
|
-
|
|
247
|
+
let keepReading = true;
|
|
248
|
+
while (keepReading) {
|
|
236
249
|
const { value, done } = await this._reader.read();
|
|
237
250
|
if (done) {
|
|
238
251
|
this._reader.releaseLock();
|
|
252
|
+
keepReading = false;
|
|
239
253
|
break;
|
|
240
254
|
}
|
|
241
255
|
if (!value || value.length === 0) {
|
|
@@ -249,11 +263,19 @@ export class ESPLoader extends EventTarget {
|
|
|
249
263
|
this._totalBytesRead += value.length;
|
|
250
264
|
}
|
|
251
265
|
}
|
|
252
|
-
catch
|
|
253
|
-
|
|
266
|
+
catch {
|
|
267
|
+
this.logger.error("Read loop got disconnected");
|
|
254
268
|
}
|
|
255
269
|
// Disconnected!
|
|
256
270
|
this.connected = false;
|
|
271
|
+
// Check if this is ESP32-S2 Native USB that needs port reselection
|
|
272
|
+
// Only trigger reconnect if initialization did NOT succeed (wrong port)
|
|
273
|
+
if (this._isESP32S2NativeUSB && !this._initializationSucceeded) {
|
|
274
|
+
this.logger.log("ESP32-S2 Native USB detected - requesting port reselection");
|
|
275
|
+
this.dispatchEvent(new CustomEvent("esp32s2-usb-reconnect", {
|
|
276
|
+
detail: { message: "ESP32-S2 Native USB requires port reselection" },
|
|
277
|
+
}));
|
|
278
|
+
}
|
|
257
279
|
this.dispatchEvent(new Event("disconnect"));
|
|
258
280
|
this.logger.debug("Finished read loop");
|
|
259
281
|
}
|
|
@@ -321,11 +343,11 @@ export class ESPLoader extends EventTarget {
|
|
|
321
343
|
* The MAC address burned into the OTP memory of the ESP chip
|
|
322
344
|
*/
|
|
323
345
|
macAddr() {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
346
|
+
const macAddr = new Array(6).fill(0);
|
|
347
|
+
const mac0 = this._efuses[0];
|
|
348
|
+
const mac1 = this._efuses[1];
|
|
349
|
+
const mac2 = this._efuses[2];
|
|
350
|
+
const mac3 = this._efuses[3];
|
|
329
351
|
let oui;
|
|
330
352
|
if (this.chipFamily == CHIP_FAMILY_ESP8266) {
|
|
331
353
|
if (mac3 != 0) {
|
|
@@ -383,9 +405,9 @@ export class ESPLoader extends EventTarget {
|
|
|
383
405
|
if (this.debug) {
|
|
384
406
|
this.logger.debug("Reading from Register " + toHex(reg, 8));
|
|
385
407
|
}
|
|
386
|
-
|
|
408
|
+
const packet = pack("<I", reg);
|
|
387
409
|
await this.sendCommand(ESP_READ_REG, packet);
|
|
388
|
-
|
|
410
|
+
const [val] = await this.getResponse(ESP_READ_REG);
|
|
389
411
|
return val;
|
|
390
412
|
}
|
|
391
413
|
/**
|
|
@@ -397,10 +419,11 @@ export class ESPLoader extends EventTarget {
|
|
|
397
419
|
async checkCommand(opcode, buffer, checksum = 0, timeout = DEFAULT_TIMEOUT) {
|
|
398
420
|
timeout = Math.min(timeout, MAX_TIMEOUT);
|
|
399
421
|
await this.sendCommand(opcode, buffer, checksum);
|
|
400
|
-
|
|
401
|
-
if (
|
|
422
|
+
const [value, responseData] = await this.getResponse(opcode, timeout);
|
|
423
|
+
if (responseData === null) {
|
|
402
424
|
throw new Error("Didn't get enough status bytes");
|
|
403
425
|
}
|
|
426
|
+
let data = responseData;
|
|
404
427
|
let statusLen = 0;
|
|
405
428
|
if (this.IS_STUB || this.chipFamily == CHIP_FAMILY_ESP8266) {
|
|
406
429
|
statusLen = 2;
|
|
@@ -435,7 +458,7 @@ export class ESPLoader extends EventTarget {
|
|
|
435
458
|
if (data.length < statusLen) {
|
|
436
459
|
throw new Error("Didn't get enough status bytes");
|
|
437
460
|
}
|
|
438
|
-
|
|
461
|
+
const status = data.slice(-statusLen, data.length);
|
|
439
462
|
data = data.slice(0, -statusLen);
|
|
440
463
|
if (this.debug) {
|
|
441
464
|
this.logger.debug("status", status);
|
|
@@ -458,7 +481,7 @@ export class ESPLoader extends EventTarget {
|
|
|
458
481
|
* does not check response
|
|
459
482
|
*/
|
|
460
483
|
async sendCommand(opcode, buffer, checksum = 0) {
|
|
461
|
-
|
|
484
|
+
const packet = slipEncode([
|
|
462
485
|
...pack("<BBHI", 0x00, opcode, buffer.length, checksum),
|
|
463
486
|
...buffer,
|
|
464
487
|
]);
|
|
@@ -477,7 +500,7 @@ export class ESPLoader extends EventTarget {
|
|
|
477
500
|
let inEscape = false;
|
|
478
501
|
let readBytes = [];
|
|
479
502
|
while (true) {
|
|
480
|
-
|
|
503
|
+
const stamp = Date.now();
|
|
481
504
|
readBytes = [];
|
|
482
505
|
while (Date.now() - stamp < timeout) {
|
|
483
506
|
if (this._inputBuffer.length > 0) {
|
|
@@ -490,12 +513,12 @@ export class ESPLoader extends EventTarget {
|
|
|
490
513
|
}
|
|
491
514
|
}
|
|
492
515
|
if (readBytes.length == 0) {
|
|
493
|
-
|
|
516
|
+
const waitingFor = partialPacket === null ? "header" : "content";
|
|
494
517
|
throw new SlipReadError("Timed out waiting for packet " + waitingFor);
|
|
495
518
|
}
|
|
496
519
|
if (this.debug)
|
|
497
520
|
this.logger.debug("Read " + readBytes.length + " bytes: " + hexFormatter(readBytes));
|
|
498
|
-
for (
|
|
521
|
+
for (const b of readBytes) {
|
|
499
522
|
if (partialPacket === null) {
|
|
500
523
|
// waiting for packet header
|
|
501
524
|
if (b == 0xc0) {
|
|
@@ -558,7 +581,7 @@ export class ESPLoader extends EventTarget {
|
|
|
558
581
|
if (packet.length < 8) {
|
|
559
582
|
continue;
|
|
560
583
|
}
|
|
561
|
-
const [resp, opRet,
|
|
584
|
+
const [resp, opRet, , val] = unpack("<BBHI", packet.slice(0, 8));
|
|
562
585
|
if (resp != 1) {
|
|
563
586
|
continue;
|
|
564
587
|
}
|
|
@@ -578,7 +601,7 @@ export class ESPLoader extends EventTarget {
|
|
|
578
601
|
* Calculate checksum of a blob, as it is defined by the ROM
|
|
579
602
|
*/
|
|
580
603
|
checksum(data, state = ESP_CHECKSUM_MAGIC) {
|
|
581
|
-
for (
|
|
604
|
+
for (const b of data) {
|
|
582
605
|
state ^= b;
|
|
583
606
|
}
|
|
584
607
|
return state;
|
|
@@ -589,11 +612,11 @@ export class ESPLoader extends EventTarget {
|
|
|
589
612
|
}
|
|
590
613
|
try {
|
|
591
614
|
// Send ESP_ROM_BAUD(115200) as the old one if running STUB otherwise 0
|
|
592
|
-
|
|
615
|
+
const buffer = pack("<II", baud, this.IS_STUB ? ESP_ROM_BAUD : 0);
|
|
593
616
|
await this.checkCommand(ESP_CHANGE_BAUDRATE, buffer);
|
|
594
617
|
}
|
|
595
618
|
catch (e) {
|
|
596
|
-
|
|
619
|
+
this.logger.error(`Baudrate change error: ${e}`);
|
|
597
620
|
throw new Error(`Unable to change the baud rate to ${baud}: No response from set baud rate command.`);
|
|
598
621
|
}
|
|
599
622
|
if (this._parent) {
|
|
@@ -636,7 +659,7 @@ export class ESPLoader extends EventTarget {
|
|
|
636
659
|
this.readLoop();
|
|
637
660
|
}
|
|
638
661
|
catch (e) {
|
|
639
|
-
|
|
662
|
+
this.logger.error(`Reconfigure port error: ${e}`);
|
|
640
663
|
throw new Error(`Unable to change the baud rate to ${baud}: ${e}`);
|
|
641
664
|
}
|
|
642
665
|
}
|
|
@@ -648,7 +671,7 @@ export class ESPLoader extends EventTarget {
|
|
|
648
671
|
async sync() {
|
|
649
672
|
for (let i = 0; i < 5; i++) {
|
|
650
673
|
this._inputBuffer.length = 0;
|
|
651
|
-
|
|
674
|
+
const response = await this._sync();
|
|
652
675
|
if (response) {
|
|
653
676
|
await sleep(SYNC_TIMEOUT);
|
|
654
677
|
return true;
|
|
@@ -666,12 +689,12 @@ export class ESPLoader extends EventTarget {
|
|
|
666
689
|
await this.sendCommand(ESP_SYNC, SYNC_PACKET);
|
|
667
690
|
for (let i = 0; i < 8; i++) {
|
|
668
691
|
try {
|
|
669
|
-
|
|
692
|
+
const [, data] = await this.getResponse(ESP_SYNC, SYNC_TIMEOUT);
|
|
670
693
|
if (data.length > 1 && data[0] == 0 && data[1] == 0) {
|
|
671
694
|
return true;
|
|
672
695
|
}
|
|
673
696
|
}
|
|
674
|
-
catch
|
|
697
|
+
catch {
|
|
675
698
|
// If read packet fails.
|
|
676
699
|
}
|
|
677
700
|
}
|
|
@@ -697,13 +720,13 @@ export class ESPLoader extends EventTarget {
|
|
|
697
720
|
async flashData(binaryData, updateProgress, offset = 0, compress = false) {
|
|
698
721
|
if (binaryData.byteLength >= 8) {
|
|
699
722
|
// unpack the (potential) image header
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
723
|
+
const header = Array.from(new Uint8Array(binaryData, 0, 4));
|
|
724
|
+
const headerMagic = header[0];
|
|
725
|
+
const headerFlashMode = header[2];
|
|
726
|
+
const headerFlashSizeFreq = header[3];
|
|
704
727
|
this.logger.log(`Image header, Magic=${toHex(headerMagic)}, FlashMode=${toHex(headerFlashMode)}, FlashSizeFreq=${toHex(headerFlashSizeFreq)}`);
|
|
705
728
|
}
|
|
706
|
-
|
|
729
|
+
const uncompressedFilesize = binaryData.byteLength;
|
|
707
730
|
let compressedFilesize = 0;
|
|
708
731
|
let dataToFlash;
|
|
709
732
|
let timeout = DEFAULT_TIMEOUT;
|
|
@@ -724,9 +747,9 @@ export class ESPLoader extends EventTarget {
|
|
|
724
747
|
let seq = 0;
|
|
725
748
|
let written = 0;
|
|
726
749
|
let position = 0;
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
750
|
+
const stamp = Date.now();
|
|
751
|
+
const flashWriteSize = this.getFlashWriteSize();
|
|
752
|
+
const filesize = compress ? compressedFilesize : uncompressedFilesize;
|
|
730
753
|
while (filesize - position > 0) {
|
|
731
754
|
if (this.debug) {
|
|
732
755
|
this.logger.log(`Writing at ${toHex(offset + seq * flashWriteSize, 8)} `);
|
|
@@ -787,8 +810,7 @@ export class ESPLoader extends EventTarget {
|
|
|
787
810
|
// Flush serial buffers before flash write operation
|
|
788
811
|
await this.flushSerialBuffers();
|
|
789
812
|
let eraseSize;
|
|
790
|
-
|
|
791
|
-
let flashWriteSize = this.getFlashWriteSize();
|
|
813
|
+
const flashWriteSize = this.getFlashWriteSize();
|
|
792
814
|
if (!this.IS_STUB &&
|
|
793
815
|
[
|
|
794
816
|
CHIP_FAMILY_ESP32,
|
|
@@ -807,22 +829,18 @@ export class ESPLoader extends EventTarget {
|
|
|
807
829
|
].includes(this.chipFamily)) {
|
|
808
830
|
await this.checkCommand(ESP_SPI_ATTACH, new Array(8).fill(0));
|
|
809
831
|
}
|
|
810
|
-
|
|
832
|
+
const numBlocks = Math.floor((size + flashWriteSize - 1) / flashWriteSize);
|
|
811
833
|
if (this.chipFamily == CHIP_FAMILY_ESP8266) {
|
|
812
834
|
eraseSize = this.getEraseSize(offset, size);
|
|
813
835
|
}
|
|
814
836
|
else {
|
|
815
837
|
eraseSize = size;
|
|
816
838
|
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
timeout = timeoutPerMb(ERASE_REGION_TIMEOUT_PER_MB, size);
|
|
823
|
-
}
|
|
824
|
-
let stamp = Date.now();
|
|
825
|
-
buffer = pack("<IIII", eraseSize, numBlocks, flashWriteSize, offset);
|
|
839
|
+
const timeout = this.IS_STUB
|
|
840
|
+
? DEFAULT_TIMEOUT
|
|
841
|
+
: timeoutPerMb(ERASE_REGION_TIMEOUT_PER_MB, size);
|
|
842
|
+
const stamp = Date.now();
|
|
843
|
+
let buffer = pack("<IIII", eraseSize, numBlocks, flashWriteSize, offset);
|
|
826
844
|
if (this.chipFamily == CHIP_FAMILY_ESP32 ||
|
|
827
845
|
this.chipFamily == CHIP_FAMILY_ESP32S2 ||
|
|
828
846
|
this.chipFamily == CHIP_FAMILY_ESP32S3 ||
|
|
@@ -858,15 +876,14 @@ export class ESPLoader extends EventTarget {
|
|
|
858
876
|
* @name flashDeflBegin
|
|
859
877
|
*
|
|
860
878
|
*/
|
|
861
|
-
async flashDeflBegin(size = 0, compressedSize = 0, offset = 0
|
|
879
|
+
async flashDeflBegin(size = 0, compressedSize = 0, offset = 0) {
|
|
862
880
|
// Start downloading compressed data to Flash (performs an erase)
|
|
863
881
|
// Returns number of blocks to write.
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
882
|
+
const flashWriteSize = this.getFlashWriteSize();
|
|
883
|
+
const numBlocks = Math.floor((compressedSize + flashWriteSize - 1) / flashWriteSize);
|
|
884
|
+
const eraseBlocks = Math.floor((size + flashWriteSize - 1) / flashWriteSize);
|
|
867
885
|
let writeSize = 0;
|
|
868
886
|
let timeout = 0;
|
|
869
|
-
let buffer;
|
|
870
887
|
if (this.IS_STUB) {
|
|
871
888
|
writeSize = size; // stub expects number of bytes here, manages erasing internally
|
|
872
889
|
timeout = timeoutPerMb(ERASE_REGION_TIMEOUT_PER_MB, writeSize); // ROM performs the erase up front
|
|
@@ -875,26 +892,26 @@ export class ESPLoader extends EventTarget {
|
|
|
875
892
|
writeSize = eraseBlocks * flashWriteSize; // ROM expects rounded up to erase block size
|
|
876
893
|
timeout = DEFAULT_TIMEOUT;
|
|
877
894
|
}
|
|
878
|
-
buffer = pack("<IIII", writeSize, numBlocks, flashWriteSize, offset);
|
|
895
|
+
const buffer = pack("<IIII", writeSize, numBlocks, flashWriteSize, offset);
|
|
879
896
|
await this.checkCommand(ESP_FLASH_DEFL_BEGIN, buffer, 0, timeout);
|
|
880
897
|
return timeout;
|
|
881
898
|
}
|
|
882
899
|
async flashFinish() {
|
|
883
|
-
|
|
900
|
+
const buffer = pack("<I", 1);
|
|
884
901
|
await this.checkCommand(ESP_FLASH_END, buffer);
|
|
885
902
|
}
|
|
886
903
|
async flashDeflFinish() {
|
|
887
|
-
|
|
904
|
+
const buffer = pack("<I", 1);
|
|
888
905
|
await this.checkCommand(ESP_FLASH_DEFL_END, buffer);
|
|
889
906
|
}
|
|
890
907
|
getBootloaderOffset() {
|
|
891
|
-
|
|
892
|
-
|
|
908
|
+
const bootFlashOffs = getSpiFlashAddresses(this.getChipFamily());
|
|
909
|
+
const BootldrFlashOffs = bootFlashOffs.flashOffs;
|
|
893
910
|
return BootldrFlashOffs;
|
|
894
911
|
}
|
|
895
912
|
async flashId() {
|
|
896
|
-
|
|
897
|
-
|
|
913
|
+
const SPIFLASH_RDID = 0x9f;
|
|
914
|
+
const result = await this.runSpiFlashCommand(SPIFLASH_RDID, [], 24);
|
|
898
915
|
return result;
|
|
899
916
|
}
|
|
900
917
|
getChipFamily() {
|
|
@@ -904,15 +921,15 @@ export class ESPLoader extends EventTarget {
|
|
|
904
921
|
let buffer = pack("<IIII", address, value, mask, delayUs);
|
|
905
922
|
if (delayAfterUs > 0) {
|
|
906
923
|
// add a dummy write to a date register as an excuse to have a delay
|
|
907
|
-
buffer.concat(pack("<IIII", getSpiFlashAddresses(this.getChipFamily()).uartDateReg, 0, 0, delayAfterUs));
|
|
924
|
+
buffer = buffer.concat(pack("<IIII", getSpiFlashAddresses(this.getChipFamily()).uartDateReg, 0, 0, delayAfterUs));
|
|
908
925
|
}
|
|
909
926
|
await this.checkCommand(ESP_WRITE_REG, buffer);
|
|
910
927
|
}
|
|
911
928
|
async setDataLengths(spiAddresses, mosiBits, misoBits) {
|
|
912
929
|
if (spiAddresses.mosiDlenOffs != -1) {
|
|
913
|
-
//
|
|
914
|
-
|
|
915
|
-
|
|
930
|
+
// Actual MCUs have a more sophisticated way to set up "user" commands
|
|
931
|
+
const SPI_MOSI_DLEN_REG = spiAddresses.regBase + spiAddresses.mosiDlenOffs;
|
|
932
|
+
const SPI_MISO_DLEN_REG = spiAddresses.regBase + spiAddresses.misoDlenOffs;
|
|
916
933
|
if (mosiBits > 0) {
|
|
917
934
|
await this.writeRegister(SPI_MOSI_DLEN_REG, mosiBits - 1);
|
|
918
935
|
}
|
|
@@ -921,18 +938,18 @@ export class ESPLoader extends EventTarget {
|
|
|
921
938
|
}
|
|
922
939
|
}
|
|
923
940
|
else {
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
941
|
+
const SPI_DATA_LEN_REG = spiAddresses.regBase + spiAddresses.usr1Offs;
|
|
942
|
+
const SPI_MOSI_BITLEN_S = 17;
|
|
943
|
+
const SPI_MISO_BITLEN_S = 8;
|
|
944
|
+
const mosiMask = mosiBits == 0 ? 0 : mosiBits - 1;
|
|
945
|
+
const misoMask = misoBits == 0 ? 0 : misoBits - 1;
|
|
946
|
+
const value = (misoMask << SPI_MISO_BITLEN_S) | (mosiMask << SPI_MOSI_BITLEN_S);
|
|
930
947
|
await this.writeRegister(SPI_DATA_LEN_REG, value);
|
|
931
948
|
}
|
|
932
949
|
}
|
|
933
950
|
async waitDone(spiCmdReg, spiCmdUsr) {
|
|
934
951
|
for (let i = 0; i < 10; i++) {
|
|
935
|
-
|
|
952
|
+
const cmdValue = await this.readRegister(spiCmdReg);
|
|
936
953
|
if ((cmdValue & spiCmdUsr) == 0) {
|
|
937
954
|
return;
|
|
938
955
|
}
|
|
@@ -948,29 +965,29 @@ export class ESPLoader extends EventTarget {
|
|
|
948
965
|
// After writing command byte, writes 'data' to MOSI and then
|
|
949
966
|
// reads back 'read_bits' of reply on MISO. Result is a number.
|
|
950
967
|
// SPI_USR register flags
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
// SPI registers, base address differs
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
968
|
+
const SPI_USR_COMMAND = 1 << 31;
|
|
969
|
+
const SPI_USR_MISO = 1 << 28;
|
|
970
|
+
const SPI_USR_MOSI = 1 << 27;
|
|
971
|
+
// SPI registers, base address differs
|
|
972
|
+
const spiAddresses = getSpiFlashAddresses(this.getChipFamily());
|
|
973
|
+
const base = spiAddresses.regBase;
|
|
974
|
+
const SPI_CMD_REG = base;
|
|
975
|
+
const SPI_USR_REG = base + spiAddresses.usrOffs;
|
|
976
|
+
const SPI_USR2_REG = base + spiAddresses.usr2Offs;
|
|
977
|
+
const SPI_W0_REG = base + spiAddresses.w0Offs;
|
|
961
978
|
// SPI peripheral "command" bitmasks for SPI_CMD_REG
|
|
962
|
-
|
|
979
|
+
const SPI_CMD_USR = 1 << 18;
|
|
963
980
|
// shift values
|
|
964
|
-
|
|
981
|
+
const SPI_USR2_COMMAND_LEN_SHIFT = 28;
|
|
965
982
|
if (readBits > 32) {
|
|
966
983
|
throw new Error("Reading more than 32 bits back from a SPI flash operation is unsupported");
|
|
967
984
|
}
|
|
968
985
|
if (data.length > 64) {
|
|
969
986
|
throw new Error("Writing more than 64 bytes of data with one SPI command is unsupported");
|
|
970
987
|
}
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
988
|
+
const dataBits = data.length * 8;
|
|
989
|
+
const oldSpiUsr = await this.readRegister(SPI_USR_REG);
|
|
990
|
+
const oldSpiUsr2 = await this.readRegister(SPI_USR2_REG);
|
|
974
991
|
let flags = SPI_USR_COMMAND;
|
|
975
992
|
if (readBits > 0) {
|
|
976
993
|
flags |= SPI_USR_MISO;
|
|
@@ -985,8 +1002,9 @@ export class ESPLoader extends EventTarget {
|
|
|
985
1002
|
await this.writeRegister(SPI_W0_REG, 0); // clear data register before we read it
|
|
986
1003
|
}
|
|
987
1004
|
else {
|
|
988
|
-
|
|
989
|
-
|
|
1005
|
+
const padLen = (4 - (data.length % 4)) % 4;
|
|
1006
|
+
data = data.concat(new Array(padLen).fill(0x00)); // pad to 32-bit multiple
|
|
1007
|
+
const words = unpack("I".repeat(Math.floor(data.length / 4)), data);
|
|
990
1008
|
let nextReg = SPI_W0_REG;
|
|
991
1009
|
this.logger.debug(`Words Length: ${words.length}`);
|
|
992
1010
|
for (const word of words) {
|
|
@@ -997,7 +1015,7 @@ export class ESPLoader extends EventTarget {
|
|
|
997
1015
|
}
|
|
998
1016
|
await this.writeRegister(SPI_CMD_REG, SPI_CMD_USR);
|
|
999
1017
|
await this.waitDone(SPI_CMD_REG, SPI_CMD_USR);
|
|
1000
|
-
|
|
1018
|
+
const status = await this.readRegister(SPI_W0_REG);
|
|
1001
1019
|
// restore some SPI controller registers
|
|
1002
1020
|
await this.writeRegister(SPI_USR_REG, oldSpiUsr);
|
|
1003
1021
|
await this.writeRegister(SPI_USR2_REG, oldSpiUsr2);
|
|
@@ -1005,9 +1023,9 @@ export class ESPLoader extends EventTarget {
|
|
|
1005
1023
|
}
|
|
1006
1024
|
async detectFlashSize() {
|
|
1007
1025
|
this.logger.log("Detecting Flash Size");
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1026
|
+
const flashId = await this.flashId();
|
|
1027
|
+
const manufacturer = flashId & 0xff;
|
|
1028
|
+
const flashIdLowbyte = (flashId >> 16) & 0xff;
|
|
1011
1029
|
this.logger.log(`FlashId: ${toHex(flashId)}`);
|
|
1012
1030
|
this.logger.log(`Flash Manufacturer: ${manufacturer.toString(16)}`);
|
|
1013
1031
|
this.logger.log(`Flash Device: ${((flashId >> 8) & 0xff).toString(16)}${flashIdLowbyte.toString(16)}`);
|
|
@@ -1020,10 +1038,10 @@ export class ESPLoader extends EventTarget {
|
|
|
1020
1038
|
* Provides a workaround for the bootloader erase bug on ESP8266.
|
|
1021
1039
|
*/
|
|
1022
1040
|
getEraseSize(offset, size) {
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1041
|
+
const sectorsPerBlock = 16;
|
|
1042
|
+
const sectorSize = FLASH_SECTOR_SIZE;
|
|
1043
|
+
const numSectors = Math.floor((size + sectorSize - 1) / sectorSize);
|
|
1044
|
+
const startSector = Math.floor(offset / sectorSize);
|
|
1027
1045
|
let headSectors = sectorsPerBlock - (startSector % sectorsPerBlock);
|
|
1028
1046
|
if (numSectors < headSectors) {
|
|
1029
1047
|
headSectors = numSectors;
|
|
@@ -1057,8 +1075,8 @@ export class ESPLoader extends EventTarget {
|
|
|
1057
1075
|
* ignore errors.
|
|
1058
1076
|
*/
|
|
1059
1077
|
async memFinish(entrypoint = 0) {
|
|
1060
|
-
|
|
1061
|
-
|
|
1078
|
+
const timeout = this.IS_STUB ? DEFAULT_TIMEOUT : MEM_END_ROM_TIMEOUT;
|
|
1079
|
+
const data = pack("<II", entrypoint == 0 ? 1 : 0, entrypoint);
|
|
1062
1080
|
return await this.checkCommand(ESP_MEM_END, data, 0, timeout);
|
|
1063
1081
|
}
|
|
1064
1082
|
async runStub(skipFlashDetection = false) {
|
|
@@ -1069,29 +1087,27 @@ export class ESPLoader extends EventTarget {
|
|
|
1069
1087
|
return this;
|
|
1070
1088
|
}
|
|
1071
1089
|
// We're transferring over USB, right?
|
|
1072
|
-
|
|
1090
|
+
const ramBlock = USB_RAM_BLOCK;
|
|
1073
1091
|
// Upload
|
|
1074
1092
|
this.logger.log("Uploading stub...");
|
|
1075
|
-
for (
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
}
|
|
1087
|
-
await this.memBlock(stub[field].slice(fromOffs, toOffs), seq);
|
|
1093
|
+
for (const field of ["text", "data"]) {
|
|
1094
|
+
const fieldData = stub[field];
|
|
1095
|
+
const offset = stub[`${field}_start`];
|
|
1096
|
+
const length = fieldData.length;
|
|
1097
|
+
const blocks = Math.floor((length + ramBlock - 1) / ramBlock);
|
|
1098
|
+
await this.memBegin(length, blocks, ramBlock, offset);
|
|
1099
|
+
for (const seq of Array(blocks).keys()) {
|
|
1100
|
+
const fromOffs = seq * ramBlock;
|
|
1101
|
+
let toOffs = fromOffs + ramBlock;
|
|
1102
|
+
if (toOffs > length) {
|
|
1103
|
+
toOffs = length;
|
|
1088
1104
|
}
|
|
1105
|
+
await this.memBlock(fieldData.slice(fromOffs, toOffs), seq);
|
|
1089
1106
|
}
|
|
1090
1107
|
}
|
|
1091
|
-
await this.memFinish(stub
|
|
1092
|
-
let pChar;
|
|
1108
|
+
await this.memFinish(stub.entry);
|
|
1093
1109
|
const p = await this.readPacket(500);
|
|
1094
|
-
pChar = String.fromCharCode(...p);
|
|
1110
|
+
const pChar = String.fromCharCode(...p);
|
|
1095
1111
|
if (pChar != "OHAI") {
|
|
1096
1112
|
throw new Error("Failed to start stub. Unexpected response: " + pChar);
|
|
1097
1113
|
}
|
|
@@ -1104,13 +1120,17 @@ export class ESPLoader extends EventTarget {
|
|
|
1104
1120
|
return espStubLoader;
|
|
1105
1121
|
}
|
|
1106
1122
|
async writeToStream(data) {
|
|
1123
|
+
if (!this.port.writable) {
|
|
1124
|
+
this.logger.debug("Port writable stream not available, skipping write");
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1107
1127
|
const writer = this.port.writable.getWriter();
|
|
1108
1128
|
await writer.write(new Uint8Array(data));
|
|
1109
1129
|
try {
|
|
1110
1130
|
writer.releaseLock();
|
|
1111
1131
|
}
|
|
1112
1132
|
catch (err) {
|
|
1113
|
-
|
|
1133
|
+
this.logger.error(`Ignoring release lock error: ${err}`);
|
|
1114
1134
|
}
|
|
1115
1135
|
}
|
|
1116
1136
|
async disconnect() {
|
|
@@ -1118,6 +1138,10 @@ export class ESPLoader extends EventTarget {
|
|
|
1118
1138
|
await this._parent.disconnect();
|
|
1119
1139
|
return;
|
|
1120
1140
|
}
|
|
1141
|
+
if (!this.port.writable) {
|
|
1142
|
+
this.logger.debug("Port already closed, skipping disconnect");
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1121
1145
|
await this.port.writable.getWriter().close();
|
|
1122
1146
|
await new Promise((resolve) => {
|
|
1123
1147
|
if (!this._reader) {
|
|
@@ -1150,7 +1174,6 @@ export class ESPLoader extends EventTarget {
|
|
|
1150
1174
|
}
|
|
1151
1175
|
this._reader = undefined;
|
|
1152
1176
|
}
|
|
1153
|
-
await sleep(SYNC_TIMEOUT);
|
|
1154
1177
|
// Close port
|
|
1155
1178
|
try {
|
|
1156
1179
|
await this.port.close();
|
|
@@ -1159,8 +1182,6 @@ export class ESPLoader extends EventTarget {
|
|
|
1159
1182
|
catch (err) {
|
|
1160
1183
|
this.logger.debug(`Port close error: ${err}`);
|
|
1161
1184
|
}
|
|
1162
|
-
// Wait for port to fully close
|
|
1163
|
-
await sleep(SYNC_TIMEOUT);
|
|
1164
1185
|
// Open the port
|
|
1165
1186
|
this.logger.debug("Opening port...");
|
|
1166
1187
|
try {
|
|
@@ -1170,8 +1191,6 @@ export class ESPLoader extends EventTarget {
|
|
|
1170
1191
|
catch (err) {
|
|
1171
1192
|
throw new Error(`Failed to open port: ${err}`);
|
|
1172
1193
|
}
|
|
1173
|
-
// Wait for port to be fully ready
|
|
1174
|
-
await sleep(SYNC_TIMEOUT);
|
|
1175
1194
|
// Verify port streams are available
|
|
1176
1195
|
if (!this.port.readable || !this.port.writable) {
|
|
1177
1196
|
throw new Error(`Port streams not available after open (readable: ${!!this.port.readable}, writable: ${!!this.port.writable})`);
|
|
@@ -1182,7 +1201,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1182
1201
|
const savedChipRevision = this.chipRevision;
|
|
1183
1202
|
const savedChipVariant = this.chipVariant;
|
|
1184
1203
|
const savedFlashSize = this.flashSize;
|
|
1185
|
-
// Reinitialize
|
|
1204
|
+
// Reinitialize
|
|
1186
1205
|
await this.hardReset(true);
|
|
1187
1206
|
if (!this._parent) {
|
|
1188
1207
|
this.__inputBuffer = [];
|
|
@@ -1191,7 +1210,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1191
1210
|
}
|
|
1192
1211
|
await this.flushSerialBuffers();
|
|
1193
1212
|
await this.sync();
|
|
1194
|
-
// Restore chip info
|
|
1213
|
+
// Restore chip info
|
|
1195
1214
|
this.chipFamily = savedChipFamily;
|
|
1196
1215
|
this.chipName = savedChipName;
|
|
1197
1216
|
this.chipRevision = savedChipRevision;
|
|
@@ -1202,14 +1221,12 @@ export class ESPLoader extends EventTarget {
|
|
|
1202
1221
|
if (!this.port.writable || !this.port.readable) {
|
|
1203
1222
|
throw new Error("Port not ready after reconnect");
|
|
1204
1223
|
}
|
|
1205
|
-
// Load stub
|
|
1224
|
+
// Load stub
|
|
1206
1225
|
const stubLoader = await this.runStub(true);
|
|
1207
1226
|
this.logger.debug("Stub loaded");
|
|
1208
1227
|
// Restore baudrate if it was changed
|
|
1209
1228
|
if (this._currentBaudRate !== ESP_ROM_BAUD) {
|
|
1210
1229
|
await stubLoader.setBaudrate(this._currentBaudRate);
|
|
1211
|
-
// Wait for port to be ready after baudrate change
|
|
1212
|
-
await sleep(SYNC_TIMEOUT);
|
|
1213
1230
|
// Verify port is still ready after baudrate change
|
|
1214
1231
|
if (!this.port.writable || !this.port.readable) {
|
|
1215
1232
|
throw new Error(`Port not ready after baudrate change (readable: ${!!this.port.readable}, writable: ${!!this.port.writable})`);
|
|
@@ -1227,18 +1244,12 @@ export class ESPLoader extends EventTarget {
|
|
|
1227
1244
|
* This clears both the application RX buffer and waits for hardware buffers to drain
|
|
1228
1245
|
*/
|
|
1229
1246
|
async flushSerialBuffers() {
|
|
1230
|
-
// Clear application
|
|
1247
|
+
// Clear application buffer
|
|
1231
1248
|
if (!this._parent) {
|
|
1232
1249
|
this.__inputBuffer = [];
|
|
1233
1250
|
}
|
|
1234
|
-
// Wait for any pending
|
|
1251
|
+
// Wait for any pending data
|
|
1235
1252
|
await sleep(SYNC_TIMEOUT);
|
|
1236
|
-
// Clear RX buffer again
|
|
1237
|
-
if (!this._parent) {
|
|
1238
|
-
this.__inputBuffer = [];
|
|
1239
|
-
}
|
|
1240
|
-
// Wait longer to ensure all stale data has been received and discarded
|
|
1241
|
-
await sleep(SYNC_TIMEOUT * 2);
|
|
1242
1253
|
// Final clear
|
|
1243
1254
|
if (!this._parent) {
|
|
1244
1255
|
this.__inputBuffer = [];
|
|
@@ -1257,20 +1268,6 @@ export class ESPLoader extends EventTarget {
|
|
|
1257
1268
|
if (!this.IS_STUB) {
|
|
1258
1269
|
throw new Error("Reading flash is only supported in stub mode. Please run runStub() first.");
|
|
1259
1270
|
}
|
|
1260
|
-
// Check if we should reconnect BEFORE starting the read
|
|
1261
|
-
// Reconnect if total bytes read >= 4MB to ensure clean state
|
|
1262
|
-
if (this._totalBytesRead >= 4 * 1024 * 1024) {
|
|
1263
|
-
this.logger.log(
|
|
1264
|
-
// `Total bytes read: ${this._totalBytesRead}. Reconnecting before new read...`,
|
|
1265
|
-
`Reconnecting before new read...`);
|
|
1266
|
-
try {
|
|
1267
|
-
await this.reconnect();
|
|
1268
|
-
}
|
|
1269
|
-
catch (err) {
|
|
1270
|
-
// If reconnect fails, throw error - don't continue with potentially broken state
|
|
1271
|
-
throw new Error(`Reconnect failed: ${err}`);
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1274
1271
|
// Flush serial buffers before flash read operation
|
|
1275
1272
|
await this.flushSerialBuffers();
|
|
1276
1273
|
this.logger.log(`Reading ${size} bytes from flash at address 0x${addr.toString(16)}...`);
|
|
@@ -1279,16 +1276,6 @@ export class ESPLoader extends EventTarget {
|
|
|
1279
1276
|
let currentAddr = addr;
|
|
1280
1277
|
let remainingSize = size;
|
|
1281
1278
|
while (remainingSize > 0) {
|
|
1282
|
-
// Reconnect every 4MB to prevent browser buffer issues
|
|
1283
|
-
if (allData.length > 0 && allData.length % (4 * 1024 * 1024) === 0) {
|
|
1284
|
-
this.logger.debug(`Read ${allData.length} bytes. Reconnecting to clear buffers...`);
|
|
1285
|
-
try {
|
|
1286
|
-
await this.reconnect();
|
|
1287
|
-
}
|
|
1288
|
-
catch (err) {
|
|
1289
|
-
throw new Error(`Reconnect failed during read: ${err}`);
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1292
1279
|
const chunkSize = Math.min(CHUNK_SIZE, remainingSize);
|
|
1293
1280
|
let chunkSuccess = false;
|
|
1294
1281
|
let retryCount = 0;
|
|
@@ -1298,8 +1285,8 @@ export class ESPLoader extends EventTarget {
|
|
|
1298
1285
|
try {
|
|
1299
1286
|
this.logger.debug(`Reading chunk at 0x${currentAddr.toString(16)}, size: 0x${chunkSize.toString(16)}`);
|
|
1300
1287
|
// Send read flash command for this chunk
|
|
1301
|
-
|
|
1302
|
-
const [res
|
|
1288
|
+
const pkt = pack("<IIII", currentAddr, chunkSize, 0x1000, 1024);
|
|
1289
|
+
const [res] = await this.checkCommand(ESP_READ_FLASH, pkt);
|
|
1303
1290
|
if (res != 0) {
|
|
1304
1291
|
throw new Error("Failed to read memory: " + res);
|
|
1305
1292
|
}
|
|
@@ -1371,7 +1358,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1371
1358
|
}
|
|
1372
1359
|
currentAddr += chunkSize;
|
|
1373
1360
|
remainingSize -= chunkSize;
|
|
1374
|
-
this.logger.debug(`Total progress: 0x${allData.length.toString(16)}
|
|
1361
|
+
this.logger.debug(`Total progress: 0x${allData.length.toString(16)} from 0x${size.toString(16)} bytes`);
|
|
1375
1362
|
}
|
|
1376
1363
|
this.logger.debug(`Successfully read ${allData.length} bytes from flash`);
|
|
1377
1364
|
return allData;
|
|
@@ -1390,17 +1377,17 @@ class EspStubLoader extends ESPLoader {
|
|
|
1390
1377
|
* @name memBegin (592)
|
|
1391
1378
|
* Start downloading an application image to RAM
|
|
1392
1379
|
*/
|
|
1393
|
-
async memBegin(size,
|
|
1394
|
-
|
|
1380
|
+
async memBegin(size, _blocks, _blocksize, offset) {
|
|
1381
|
+
const stub = await getStubCode(this.chipFamily, this.chipRevision);
|
|
1395
1382
|
// Stub may be null for chips without stub support
|
|
1396
1383
|
if (stub === null) {
|
|
1397
|
-
return;
|
|
1384
|
+
return [0, []];
|
|
1398
1385
|
}
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
for (
|
|
1386
|
+
const load_start = offset;
|
|
1387
|
+
const load_end = offset + size;
|
|
1388
|
+
this.logger.debug(`Load range: ${toHex(load_start, 8)}-${toHex(load_end, 8)}`);
|
|
1389
|
+
this.logger.debug(`Stub data: ${toHex(stub.data_start, 8)}, len: ${stub.data.length}, text: ${toHex(stub.text_start, 8)}, len: ${stub.text.length}`);
|
|
1390
|
+
for (const [start, end] of [
|
|
1404
1391
|
[stub.data_start, stub.data_start + stub.data.length],
|
|
1405
1392
|
[stub.text_start, stub.text_start + stub.text.length],
|
|
1406
1393
|
]) {
|
|
@@ -1418,6 +1405,7 @@ class EspStubLoader extends ESPLoader {
|
|
|
1418
1405
|
"Try changing the binary loading address.");
|
|
1419
1406
|
}
|
|
1420
1407
|
}
|
|
1408
|
+
return [0, []];
|
|
1421
1409
|
}
|
|
1422
1410
|
/**
|
|
1423
1411
|
* @name getEraseSize
|