Skip to content

Commit

Permalink
add before after reset modes
Browse files Browse the repository at this point in the history
  • Loading branch information
brianignacio5 committed Jun 17, 2024
1 parent 265ece5 commit b884a1f
Show file tree
Hide file tree
Showing 8 changed files with 285 additions and 149 deletions.
246 changes: 98 additions & 148 deletions src/esploader.ts
Original file line number Diff line number Diff line change
@@ -1,114 +1,13 @@
import { ESPError } from "./error.js";
import { ESPError } from "./types/error.js";
import { Data, deflate, Inflate } from "pako";
import { Transport, SerialOptions } from "./webserial.js";
import { ROM } from "./targets/rom.js";
import { customReset, usbJTAGSerialReset } from "./reset.js";
import { classicReset, customReset, hardReset, ResetFunctions, usbJTAGSerialReset } from "./reset.js";
import atob from "atob-lite";

/* global SerialPort */

/**
* Options for flashing a device with firmware.
* @interface FlashOptions
*/
export interface FlashOptions {
/**
* An array of file objects representing the data to be flashed.
* @type {Array<{ data: string; address: number }>}
*/
fileArray: { data: string; address: number }[];

/**
* The size of the flash memory to be used.
* @type {string}
*/
flashSize: string;

/**
* The flash mode to be used (e.g., QIO, QOUT, DIO, DOUT).
* @type {string}
*/
flashMode: string;

/**
* The flash frequency to be used (e.g., 40MHz, 80MHz).
* @type {string}
*/
flashFreq: string;

/**
* Flag indicating whether to erase all existing data in the flash memory before flashing.
* @type {boolean}
*/
eraseAll: boolean;

/**
* Flag indicating whether to compress the data before flashing.
* @type {boolean}
*/
compress: boolean;

/**
* A function to report the progress of the flashing operation (optional).
* @type {(fileIndex: number, written: number, total: number) => void}
*/
reportProgress?: (fileIndex: number, written: number, total: number) => void;

/**
* A function to calculate the MD5 hash of the firmware image (optional).
* @type {(image: string) => string}
*/
calculateMD5Hash?: (image: string) => string;
}

/**
* Options to configure ESPLoader.
* @interface LoaderOptions
*/
export interface LoaderOptions {
/**
* The transport mechanism to communicate with the device.
* @type {Transport}
*/
transport: Transport;

/**
* The port to initialize the transport class.
* @type {SerialPort}
*/
port?: SerialPort;

/**
* Set of options for SerialPort class.
* @type {Transport}
*/
serialOptions?: SerialOptions;

/**
* The baud rate to be used for communication with the device.
* @type {number}
*/
baudrate: number;

/**
* An optional terminal interface to interact with the loader during the process.
* @type {IEspLoaderTerminal}
*/
terminal?: IEspLoaderTerminal;

/**
* The baud rate to be used during the initial ROM communication with the device.
* @type {number}
*/
romBaudrate: number;

/**
* Flag indicating whether to enable debug logging for the loader (optional).
* @type {boolean}
*/
debugLogging?: boolean;
enableTracing?: boolean;
}
import { IEspLoaderTerminal } from "./types/loaderTerminal.js";
import { LoaderOptions } from "./types/loaderOptions.js";
import { FlashOptions } from "./types/flashOptions.js";
import { After, Before } from "./types/resetModes.js";

type FlashReadCallback = ((packet: Uint8Array, progress: number, totalSize: number) => void) | null;

Expand Down Expand Up @@ -160,29 +59,6 @@ async function magic2Chip(magic: number): Promise<ROM | null> {
}
}

/**
* A wrapper around your implementation of a terminal by
* implementing the clean, write and writeLine methods
* which are called by the ESPLoader class.
* @interface IEspLoaderTerminal
*/
export interface IEspLoaderTerminal {
/**
* Execute a terminal clean command.
*/
clean: () => void;
/**
* Write a string of data that include a line terminator.
* @param {string} data - The string to write with line terminator.
*/
writeLine: (data: string) => void;
/**
* Write a string of data.
* @param {string} data - The string to write.
*/
write: (data: string) => void;
}

export class ESPLoader {
ESP_RAM_BLOCK = 0x1800;
ESP_FLASH_BEGIN = 0x02;
Expand Down Expand Up @@ -255,6 +131,7 @@ export class ESPLoader {
private romBaudrate = 115200;
private debugLogging = false;
private syncStubDetected = false;
private resetFunctions: ResetFunctions;

/**
* Create a new ESPLoader to perform serial communication
Expand All @@ -270,6 +147,12 @@ export class ESPLoader {

this.transport = options.transport;
this.baudrate = options.baudrate;
this.resetFunctions = {
classicReset,
customReset,
hardReset,
usbJTAGSerialReset,
};
if (options.serialOptions) {
this.serialOptions = options.serialOptions;
}
Expand All @@ -291,6 +174,19 @@ export class ESPLoader {
this.transport.tracing = options.enableTracing;
}

if (options.resetFunctions?.classicReset) {
this.resetFunctions.classicReset = options.resetFunctions?.classicReset;
}
if (options.resetFunctions?.customReset) {
this.resetFunctions.customReset = options.resetFunctions?.customReset;
}
if (options.resetFunctions?.hardReset) {
this.resetFunctions.hardReset = options.resetFunctions?.hardReset;
}
if (options.resetFunctions?.usbJTAGSerialReset) {
this.resetFunctions.usbJTAGSerialReset = options.resetFunctions?.usbJTAGSerialReset;
}

this.info("esptool.js");
this.info("Serial port " + this.transport.getInfo());
}
Expand Down Expand Up @@ -594,18 +490,9 @@ export class ESPLoader {
* @param {boolean} esp32r0Delay - Enable delay for ESP32 R0
* @returns {string} - Returns 'success' or 'error' message.
*/
async _connectAttempt(mode = "default_reset", esp32r0Delay = false) {
async _connectAttempt(mode: Before = "default_reset", esp32r0Delay = false) {
this.debug("_connect_attempt " + mode + " " + esp32r0Delay);
if (mode !== "no_reset") {
if (this.transport.getPid() === this.USB_JTAG_SERIAL_PID) {
// Custom reset sequence, which is required when the device
// is connecting via its USB-JTAG-Serial peripheral
await usbJTAGSerialReset(this.transport);
} else {
const strSequence = esp32r0Delay ? "D0|R1|W100|W2000|D1|R0|W50|D0" : "D0|R1|W100|D1|R0|W50|D0";
await customReset(this.transport, strSequence);
}
}
await this.constructResetSequence(mode, esp32r0Delay);
let i = 0;
let keepReading = true;
while (keepReading) {
Expand Down Expand Up @@ -643,13 +530,37 @@ export class ESPLoader {
return "error";
}

/**
* Constructs a sequence of reset strategies based on the OS,
* used ESP chip, external settings, and environment variables.
* Returns a tuple of one or more reset strategies to be tried sequentially.
* @param {string} mode - Reset mode to use
* @param {boolean} esp32r0Delay - Enable delay for ESP32 R0
*/
async constructResetSequence(mode: Before, esp32r0Delay: boolean) {
if (mode !== "no_reset") {
if (mode === "usb_reset" || this.transport.getPid() === this.USB_JTAG_SERIAL_PID) {
// Custom reset sequence, which is required when the device
// is connecting via its USB-JTAG-Serial peripheral
if (this.resetFunctions.usbJTAGSerialReset) {
await this.resetFunctions.usbJTAGSerialReset(this.transport);
}
} else {
const strSequence = esp32r0Delay ? "D0|R1|W100|W2000|D1|R0|W50|D0" : "D0|R1|W100|D1|R0|W50|D0";
if (this.resetFunctions.customReset) {
await this.resetFunctions.customReset(this.transport, strSequence);
}
}
}
}

/**
* Perform a connection to chip.
* @param {string} mode - Reset mode to use. Example: 'default_reset' | 'no_reset'
* @param {number} attempts - Number of connection attempts
* @param {boolean} detecting - Detect the connected chip
*/
async connect(mode = "default_reset", attempts = 7, detecting = false) {
async connect(mode: Before = "default_reset", attempts = 7, detecting = false) {
let i;
let resp;
this.info("Connecting...", false);
Expand Down Expand Up @@ -685,7 +596,7 @@ export class ESPLoader {
* Connect and detect the existing chip.
* @param {string} mode Reset mode to use for connection.
*/
async detectChip(mode = "default_reset") {
async detectChip(mode: Before = "default_reset") {
await this.connect(mode);
this.info("Detecting chip type... ", false);
if (this.chip != null) {
Expand Down Expand Up @@ -1234,7 +1145,7 @@ export class ESPLoader {
* @param {string} mode Reset mode to use
* @returns {string} chip ROM
*/
async main(mode = "default_reset") {
async main(mode: Before = "default_reset") {
await this.detectChip(mode);

const chip = await this.chip.getChipDescription(this);
Expand Down Expand Up @@ -1514,18 +1425,57 @@ export class ESPLoader {

/**
* Soft reset the device chip. Soft reset with run user code is the closest.
* @param {boolean} stayInBootloader Flag to indicate if to stay in bootloader
*/
async softReset() {
async softReset(stayInBootloader: boolean) {
if (!this.IS_STUB) {
if (stayInBootloader) {
return; // ROM bootloader is already in bootloader!
}
// "run user code" is as close to a soft reset as we can do
await this.flashBegin(0, 0);
await this.flashFinish(false);
} else if (this.chip.CHIP_NAME != "ESP8266") {
throw new ESPError("Soft resetting is currently only supported on ESP8266");
} else {
// running user code from stub loader requires some hacks
// in the stub loader
await this.command(this.ESP_RUN_USER_CODE, undefined, undefined, false);
if (stayInBootloader) {
// soft resetting from the stub loader
// will re-load the ROM bootloader
await this.flashBegin(0, 0);
await this.flashFinish(true);
} else {
// running user code from stub loader requires some hacks
// in the stub loader
await this.command(this.ESP_RUN_USER_CODE, undefined, undefined, false);
}
}
}

/**
* Execute this function to execute after operation reset functions.
* @param {After} mode After operation mode
* @param { boolean } usingUsbOtg For 'hard_reset' to specify if using USB-OTG
*/
async after(mode: After, usingUsbOtg?: boolean) {
switch (mode) {
case "hard_reset":
if (this.resetFunctions.hardReset) {
await this.resetFunctions.hardReset(this.transport, usingUsbOtg);
}
break;
case "soft_reset":
this.info("Soft resetting...");
await this.softReset(false);
break;
case "no_reset_stub":
this.info("Staying in flasher stub.");
break;
default:
this.info("Staying in bootloader.");
if (this.IS_STUB) {
this.softReset(true);
}
break;
}
}
}
7 changes: 6 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
export { IEspLoaderTerminal, ESPLoader, FlashOptions, LoaderOptions } from "./esploader.js";
export { ESPLoader } from "./esploader.js";
export {
classicReset,
customReset,
hardReset,
usbJTAGSerialReset,
validateCustomResetStringSequence,
ResetFunctions,
} from "./reset.js";
export { ROM } from "./targets/rom.js";
export { Transport, SerialOptions } from "./webserial.js";
export { LoaderOptions } from "./types/loaderOptions.js";
export { FlashOptions } from "./types/flashOptions.js";
export { IEspLoaderTerminal } from "./types/loaderTerminal.js";
export { Before, After } from "./types/resetModes.js";
33 changes: 33 additions & 0 deletions src/reset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,39 @@ import { Transport } from "./webserial.js";

const DEFAULT_RESET_DELAY = 50;

/**
* Set of reset functions for ESP Loader connection.
* @interface ResetFunctions
*/
export interface ResetFunctions {
/**
* Execute a classic set of commands that will reset the chip.
* @param transport Transport class to perform serial communication.
* @param resetDelay Delay in milliseconds for reset.
*/
classicReset?: (transport: Transport, resetDelay?: number) => Promise<void>;

/**
* Execute a set of commands for USB JTAG serial reset.
* @param transport Transport class to perform serial communication.
*/
usbJTAGSerialReset?: (transport: Transport) => Promise<void>;

/**
* Execute a classic set of commands that will reset the chip.
* @param transport Transport class to perform serial communication.
* @param {boolean} usingUsbOtg is it using USB-OTG ?
*/
hardReset?: (transport: Transport, usingUsbOtg?: boolean) => Promise<void>;

/**
* Execute a custom set of commands that will reset the chip.
* @param transport Transport class to perform serial communication.
* @param {string} sequenceString Custom string sequence for reset strategy
*/
customReset?: (transport: Transport, sequenceString: string) => Promise<void>;
}

/**
* Sleep for ms milliseconds
* @param {number} ms Milliseconds to wait
Expand Down
File renamed without changes.
Loading

0 comments on commit b884a1f

Please sign in to comment.