esp32tool 1.1.2 → 1.1.4
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/dist/esp_loader.d.ts +16 -0
- package/dist/esp_loader.js +108 -34
- package/dist/web/index.js +1 -1
- package/js/modules/esptool.js +1 -1
- package/package.json +8 -3
- package/src/esp_loader.ts +135 -39
- package/tools/forge-fuses-plugin.cjs +53 -0
package/src/esp_loader.ts
CHANGED
|
@@ -166,8 +166,6 @@ export class ESPLoader extends EventTarget {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
async initialize() {
|
|
169
|
-
await this.hardReset(true);
|
|
170
|
-
|
|
171
169
|
if (!this._parent) {
|
|
172
170
|
this.__inputBuffer = [];
|
|
173
171
|
this.__totalBytesRead = 0;
|
|
@@ -196,13 +194,8 @@ export class ESPLoader extends EventTarget {
|
|
|
196
194
|
this.readLoop();
|
|
197
195
|
}
|
|
198
196
|
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
await this.drainInputBuffer(200);
|
|
202
|
-
|
|
203
|
-
// Clear buffer again after starting read loop
|
|
204
|
-
await this.flushSerialBuffers();
|
|
205
|
-
await this.sync();
|
|
197
|
+
// Try to connect with different reset strategies
|
|
198
|
+
await this.connectWithResetStrategies();
|
|
206
199
|
|
|
207
200
|
// Detect chip type
|
|
208
201
|
await this.detectChip();
|
|
@@ -464,37 +457,11 @@ export class ESPLoader extends EventTarget {
|
|
|
464
457
|
if (bootloader) {
|
|
465
458
|
// enter flash mode
|
|
466
459
|
if (this.port.getInfo().usbProductId === USB_JTAG_SERIAL_PID) {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
// to enter flash mode automatically.
|
|
470
|
-
await this.setDTR(false);
|
|
471
|
-
await this.setRTS(false);
|
|
472
|
-
await this.sleep(100);
|
|
473
|
-
|
|
474
|
-
await this.setDTR(true);
|
|
475
|
-
await this.setRTS(false);
|
|
476
|
-
await this.sleep(100);
|
|
477
|
-
|
|
478
|
-
await this.setRTS(true);
|
|
479
|
-
await this.setDTR(false);
|
|
480
|
-
await this.setRTS(true);
|
|
481
|
-
|
|
482
|
-
await this.sleep(100);
|
|
483
|
-
await this.setDTR(false);
|
|
484
|
-
await this.setRTS(false);
|
|
485
|
-
this.logger.log("USB MCU reset.");
|
|
460
|
+
await this.hardResetUSBJTAGSerial();
|
|
461
|
+
this.logger.log("USB-JTAG/Serial reset.");
|
|
486
462
|
} else {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
// use normal way to enter flash mode.
|
|
490
|
-
await this.setDTR(false);
|
|
491
|
-
await this.setRTS(true);
|
|
492
|
-
await this.sleep(100);
|
|
493
|
-
await this.setDTR(true);
|
|
494
|
-
await this.setRTS(false);
|
|
495
|
-
await this.sleep(50);
|
|
496
|
-
await this.setDTR(false);
|
|
497
|
-
this.logger.log("DTR/RTS USB serial chip reset.");
|
|
463
|
+
await this.hardResetClassic();
|
|
464
|
+
this.logger.log("Classic reset.");
|
|
498
465
|
}
|
|
499
466
|
} else {
|
|
500
467
|
// just reset
|
|
@@ -879,6 +846,135 @@ export class ESPLoader extends EventTarget {
|
|
|
879
846
|
}
|
|
880
847
|
}
|
|
881
848
|
|
|
849
|
+
/**
|
|
850
|
+
* @name connectWithResetStrategies
|
|
851
|
+
* Try different reset strategies to enter bootloader mode
|
|
852
|
+
* Similar to esptool.py's connect() method with multiple reset strategies
|
|
853
|
+
*/
|
|
854
|
+
async connectWithResetStrategies() {
|
|
855
|
+
const portInfo = this.port.getInfo();
|
|
856
|
+
const isUSBJTAGSerial = portInfo.usbProductId === USB_JTAG_SERIAL_PID;
|
|
857
|
+
const isEspressifUSB = portInfo.usbVendorId === 0x303a;
|
|
858
|
+
|
|
859
|
+
this.logger.log(
|
|
860
|
+
`Detected USB: VID=0x${portInfo.usbVendorId?.toString(16) || "unknown"}, PID=0x${portInfo.usbProductId?.toString(16) || "unknown"}`,
|
|
861
|
+
);
|
|
862
|
+
|
|
863
|
+
// Define reset strategies to try in order
|
|
864
|
+
const resetStrategies: Array<{ name: string; fn: () => Promise<void> }> =
|
|
865
|
+
[];
|
|
866
|
+
|
|
867
|
+
// Strategy 1: USB-JTAG/Serial reset (for ESP32-C3, C6, S3, etc.)
|
|
868
|
+
// Try this first if we detect Espressif USB VID or the specific PID
|
|
869
|
+
if (isUSBJTAGSerial || isEspressifUSB) {
|
|
870
|
+
resetStrategies.push({
|
|
871
|
+
name: "USB-JTAG/Serial",
|
|
872
|
+
fn: async () => await this.hardResetUSBJTAGSerial(),
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
// Strategy 2: Classic reset (for USB-to-Serial bridges)
|
|
877
|
+
resetStrategies.push({
|
|
878
|
+
name: "Classic",
|
|
879
|
+
fn: async () => await this.hardResetClassic(),
|
|
880
|
+
});
|
|
881
|
+
|
|
882
|
+
// Strategy 3: If USB-JTAG/Serial was not tried yet, try it as fallback
|
|
883
|
+
if (!isUSBJTAGSerial && !isEspressifUSB) {
|
|
884
|
+
resetStrategies.push({
|
|
885
|
+
name: "USB-JTAG/Serial (fallback)",
|
|
886
|
+
fn: async () => await this.hardResetUSBJTAGSerial(),
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
let lastError: Error | null = null;
|
|
891
|
+
|
|
892
|
+
// Try each reset strategy
|
|
893
|
+
for (const strategy of resetStrategies) {
|
|
894
|
+
try {
|
|
895
|
+
this.logger.log(`Trying ${strategy.name} reset...`);
|
|
896
|
+
|
|
897
|
+
// Check if port is still open, if not, skip this strategy
|
|
898
|
+
if (!this.connected || !this.port.writable) {
|
|
899
|
+
this.logger.log(`Port disconnected, skipping ${strategy.name} reset`);
|
|
900
|
+
continue;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
await strategy.fn();
|
|
904
|
+
|
|
905
|
+
// Try to sync after reset
|
|
906
|
+
await this.sync();
|
|
907
|
+
|
|
908
|
+
// If we get here, sync succeeded
|
|
909
|
+
this.logger.log(`Connected successfully with ${strategy.name} reset.`);
|
|
910
|
+
return;
|
|
911
|
+
} catch (error) {
|
|
912
|
+
lastError = error as Error;
|
|
913
|
+
this.logger.log(
|
|
914
|
+
`${strategy.name} reset failed: ${(error as Error).message}`,
|
|
915
|
+
);
|
|
916
|
+
|
|
917
|
+
// If port got disconnected, we can't try more strategies
|
|
918
|
+
if (!this.connected || !this.port.writable) {
|
|
919
|
+
this.logger.log(`Port disconnected during reset attempt`);
|
|
920
|
+
break;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// Clear buffers before trying next strategy
|
|
924
|
+
this._inputBuffer.length = 0;
|
|
925
|
+
await this.drainInputBuffer(200);
|
|
926
|
+
await this.flushSerialBuffers();
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// All strategies failed
|
|
931
|
+
throw new Error(
|
|
932
|
+
`Couldn't sync to ESP. Try resetting manually. Last error: ${lastError?.message}`,
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/**
|
|
937
|
+
* @name hardResetUSBJTAGSerial
|
|
938
|
+
* USB-JTAG/Serial reset sequence for ESP32-C3, ESP32-S3, ESP32-C6, etc.
|
|
939
|
+
*/
|
|
940
|
+
async hardResetUSBJTAGSerial() {
|
|
941
|
+
await this.setRTS(false);
|
|
942
|
+
await this.setDTR(false); // Idle
|
|
943
|
+
await this.sleep(100);
|
|
944
|
+
|
|
945
|
+
await this.setDTR(true); // Set IO0
|
|
946
|
+
await this.setRTS(false);
|
|
947
|
+
await this.sleep(100);
|
|
948
|
+
|
|
949
|
+
await this.setRTS(true); // Reset. Calls inverted to go through (1,1) instead of (0,0)
|
|
950
|
+
await this.setDTR(false);
|
|
951
|
+
await this.setRTS(true); // RTS set as Windows only propagates DTR on RTS setting
|
|
952
|
+
await this.sleep(100);
|
|
953
|
+
|
|
954
|
+
await this.setDTR(false);
|
|
955
|
+
await this.setRTS(false); // Chip out of reset
|
|
956
|
+
|
|
957
|
+
// Wait for chip to boot into bootloader
|
|
958
|
+
await this.sleep(200);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* @name hardResetClassic
|
|
963
|
+
* Classic reset sequence for USB-to-Serial bridge chips (CH340, CP2102, etc.)
|
|
964
|
+
*/
|
|
965
|
+
async hardResetClassic() {
|
|
966
|
+
await this.setDTR(false); // IO0=HIGH
|
|
967
|
+
await this.setRTS(true); // EN=LOW, chip in reset
|
|
968
|
+
await this.sleep(100);
|
|
969
|
+
await this.setDTR(true); // IO0=LOW
|
|
970
|
+
await this.setRTS(false); // EN=HIGH, chip out of reset
|
|
971
|
+
await this.sleep(50);
|
|
972
|
+
await this.setDTR(false); // IO0=HIGH, done
|
|
973
|
+
|
|
974
|
+
// Wait for chip to boot into bootloader
|
|
975
|
+
await this.sleep(200);
|
|
976
|
+
}
|
|
977
|
+
|
|
882
978
|
/**
|
|
883
979
|
* @name sync
|
|
884
980
|
* Put into ROM bootload mode & attempt to synchronize with the
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
const { PluginBase, namedHookWithTaskFn } = require('@electron-forge/plugin-base');
|
|
3
|
+
|
|
4
|
+
function getElectronExecutablePath({ appName, basePath, platform }) {
|
|
5
|
+
if (['darwin', 'mas'].includes(platform)) {
|
|
6
|
+
return path.join(basePath, 'MacOS', appName);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const suffix = platform === 'win32' ? '.exe' : '';
|
|
10
|
+
return path.join(basePath, `${appName}${suffix}`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class FusesPlugin extends PluginBase {
|
|
14
|
+
constructor(fusesConfig) {
|
|
15
|
+
super(fusesConfig);
|
|
16
|
+
this.name = 'fuses';
|
|
17
|
+
this.fusesConfig = fusesConfig ?? {};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getHooks() {
|
|
21
|
+
return {
|
|
22
|
+
packageAfterCopy: namedHookWithTaskFn(
|
|
23
|
+
async (listrTask, resolvedForgeConfig, resourcesPath, electronVersion, platform, arch) => {
|
|
24
|
+
if (!Object.keys(this.fusesConfig).length) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const applePlatforms = ['darwin', 'mas'];
|
|
29
|
+
const pathToElectronExecutable = getElectronExecutablePath({
|
|
30
|
+
appName: applePlatforms.includes(platform) ? 'Electron' : 'electron',
|
|
31
|
+
basePath: path.resolve(resourcesPath, '../..'),
|
|
32
|
+
platform,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const osxSignConfig = resolvedForgeConfig.packagerConfig.osxSign;
|
|
36
|
+
const hasOSXSignConfig =
|
|
37
|
+
(typeof osxSignConfig === 'object' && Boolean(Object.keys(osxSignConfig).length)) ||
|
|
38
|
+
Boolean(osxSignConfig);
|
|
39
|
+
|
|
40
|
+
const { flipFuses } = await import('@electron/fuses');
|
|
41
|
+
await flipFuses(pathToElectronExecutable, {
|
|
42
|
+
resetAdHocDarwinSignature:
|
|
43
|
+
!hasOSXSignConfig && applePlatforms.includes(platform) && arch === 'arm64',
|
|
44
|
+
...this.fusesConfig,
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
'Flipping Fuses'
|
|
48
|
+
),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = { FusesPlugin };
|