diff --git a/examples/typescript/src/index.ts b/examples/typescript/src/index.ts index 019add9..8dc41bb 100644 --- a/examples/typescript/src/index.ts +++ b/examples/typescript/src/index.ts @@ -23,7 +23,7 @@ const alertDiv = document.getElementById("alertDiv"); // This is a frontend example of Esptool-JS using local bundle file // To optimize use a CDN hosted version like // https://unpkg.com/esptool-js/bundle.js -import { ESPLoader, FlashOptions, LoaderOptions, WebSerialTransport, SerialOptions } from "../../../lib"; +import { ESPLoader, FlashOptions, LoaderOptions, WebSerialTransport, SerialOptions, ITrace } from "../../../lib"; declare let Terminal; // Terminal is imported in HTML script declare let CryptoJS; // CryptoJS is imported in HTML script @@ -79,10 +79,35 @@ const espLoaderTerminal = { }, }; +class TraceObject implements ITrace { + traceBuffer: string; + private lastTraceTime = Date.now(); + + trace(message: string) { + const delta = Date.now() - this.lastTraceTime; + const prefix = `TRACE ${delta.toFixed(3)}`; + const traceMessage = `${prefix} ${message}`; + console.log(traceMessage); + this.traceBuffer += traceMessage + "\n"; + } + + async returnTrace() { + try { + await navigator.clipboard.writeText(this.traceBuffer); + console.log("Text copied to clipboard!"); + } catch (err) { + console.error("Failed to copy text:", err); + } + return this.traceBuffer; + } +} + +const traceObj = new TraceObject(); + connectButton.onclick = async () => { if (device === null) { device = await navigator.serial.requestPort({}); - transport = new WebSerialTransport(device, true); + transport = new WebSerialTransport(device, traceObj); } const serialOptions = { baudRate: parseInt(baudrates.value) } as SerialOptions; @@ -118,8 +143,8 @@ connectButton.onclick = async () => { }; traceButton.onclick = async () => { - if (transport) { - transport.returnTrace(); + if (traceObj) { + traceObj.returnTrace(); } }; @@ -233,7 +258,7 @@ let isConsoleClosed = false; consoleStartButton.onclick = async () => { if (device === null) { device = await navigator.serial.requestPort({}); - transport = new WebSerialTransport(device, true); + transport = new WebSerialTransport(device, traceObj); } lblConsoleFor.style.display = "block"; lblConsoleBaudrate.style.display = "none"; @@ -248,7 +273,7 @@ consoleStartButton.onclick = async () => { isConsoleClosed = false; while (true && !isConsoleClosed) { - const val = await transport.rawRead(); + const val = await transport.read(); if (typeof val !== "undefined") { term.write(val); } else { diff --git a/src/esploader.ts b/src/esploader.ts index bf2d71c..d6a4e01 100644 --- a/src/esploader.ts +++ b/src/esploader.ts @@ -7,6 +7,7 @@ import { classicReset, customReset, hardReset, usbJTAGSerialReset } from "./rese import { hexConvert } from "./utils/hex"; import { appendArray, bstrToUi8, byteArrayToInt, intToByteArray, shortToBytearray, ui8ToBstr } from "./utils/convert"; import { Slip } from "./utils/slip"; +import { ITrace } from "./utils/ITrace"; /** * Options for flashing a device with firmware. @@ -134,6 +135,12 @@ export interface LoaderOptions { * @type {ResetFunctions} */ resetFunctions?: ResetFunctions; + + /** + * The Trace object to log all communication output. + * @type {ITrace} + */ + tracer?: ITrace; } /** @@ -287,6 +294,7 @@ export class ESPLoader { private debugLogging = false; private resetFunctions: ResetFunctions; private slip: Slip; + private tracer?: ITrace; /** * Create a new ESPLoader to perform serial communication @@ -329,6 +337,9 @@ export class ESPLoader { if (typeof options.debugLogging !== "undefined") { this.debugLogging = options.debugLogging; } + if (options.tracer) { + this.tracer = options.tracer; + } this.slip = new Slip(this.transport); this.info("esptool.js"); @@ -440,8 +451,8 @@ export class ESPLoader { timeout: number = 3000, ): Promise<[number, Uint8Array]> { if (op != null) { - if (this.transport.tracing) { - this.transport.trace( + if (this.tracer) { + this.tracer.trace( `command op:0x${op.toString(16).padStart(2, "0")} data len=${data.length} wait_response=${ waitResponse ? 1 : 0 } timeout=${(timeout / 1000).toFixed(3)} data=${hexConvert(data)}`, diff --git a/src/index.ts b/src/index.ts index 538e13d..b4e904d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,3 +11,4 @@ export { ROM } from "./targets/rom"; export { ISerialTransport, ISerialOptions } from "./transport/ISerialTransport"; export { SerialOptions, WebSerialTransport } from "./transport/WebSerialTransport"; export { Slip, SlipReaderOutput } from "./utils/slip"; +export { ITrace } from "./utils/ITrace"; diff --git a/src/transport/ISerialTransport.ts b/src/transport/ISerialTransport.ts index 0f069e3..6cb7299 100644 --- a/src/transport/ISerialTransport.ts +++ b/src/transport/ISerialTransport.ts @@ -16,7 +16,6 @@ export interface ISerialOptions { * @interface ISerialTransport */ export interface ISerialTransport { - tracing: boolean; leftOver: Uint8Array; /** @@ -31,12 +30,6 @@ export interface ISerialTransport { */ getPID(): number | undefined; - /** - * Format received or sent data for tracing output. - * @param {string} message Message to format as trace line. - */ - trace(message: string): void; - /** * Write binary data to device. * @param {Uint8Array} data 8 bit unsigned data array to write to device. diff --git a/src/transport/WebSerialTransport.ts b/src/transport/WebSerialTransport.ts index 28de6e3..7838066 100644 --- a/src/transport/WebSerialTransport.ts +++ b/src/transport/WebSerialTransport.ts @@ -3,6 +3,7 @@ import { ISerialTransport, ISerialOptions } from "./ISerialTransport"; import { hexConvert } from "../utils/hex"; import { appendArray } from "../utils/convert"; +import { ITrace } from "../utils/ITrace"; /** * Options for device serialPort. @@ -51,11 +52,9 @@ export interface SerialOptions extends ISerialOptions { */ export class WebSerialTransport implements ISerialTransport { public leftOver = new Uint8Array(0); - private traceLog = ""; - private lastTraceTime = Date.now(); private reader: ReadableStreamDefaultReader | undefined; - constructor(public device: SerialPort, public tracing = false) {} + constructor(public device: SerialPort, private tracer?: ITrace) {} /** * Request the serial device vendor ID and Product ID as string. @@ -76,30 +75,6 @@ export class WebSerialTransport implements ISerialTransport { return this.device.getInfo().usbProductId; } - /** - * Format received or sent data for tracing output. - * @param {string} message Message to format as trace line. - */ - trace(message: string) { - const delta = Date.now() - this.lastTraceTime; - const prefix = `TRACE ${delta.toFixed(3)}`; - const traceMessage = `${prefix} ${message}`; - console.log(traceMessage); - this.traceLog += traceMessage + "\n"; - } - - /** - * Return the whole trace output to the user clipboard. - */ - async returnTrace() { - try { - await navigator.clipboard.writeText(this.traceLog); - console.log("Text copied to clipboard!"); - } catch (err) { - console.error("Failed to copy text:", err); - } - } - /** * Write binary data to device using the WebSerial device writable stream. * @param {Uint8Array} outData 8 bit unsigned data array to write to device. @@ -107,9 +82,9 @@ export class WebSerialTransport implements ISerialTransport { async write(outData: Uint8Array) { if (this.device.writable) { const writer = this.device.writable.getWriter(); - if (this.tracing) { - console.log("Write bytes"); - this.trace(`Write ${outData.length} bytes: ${hexConvert(outData)}`); + if (this.tracer) { + this.tracer.trace("Write bytes"); + this.tracer.trace(`Write ${outData.length} bytes: ${hexConvert(outData)}`); } await writer.write(outData); writer.releaseLock(); @@ -152,9 +127,9 @@ export class WebSerialTransport implements ISerialTransport { this.leftOver = packet; throw new Error("Timeout"); } - if (this.tracing) { - console.log("Raw Read bytes"); - this.trace(`Read ${value.length} bytes: ${hexConvert(value)}`); + if (this.tracer) { + this.tracer.trace("Raw Read bytes"); + this.tracer.trace(`Read ${value.length} bytes: ${hexConvert(value)}`); } const p = appendArray(packet, value); packet = p; diff --git a/src/utils/ITrace.ts b/src/utils/ITrace.ts new file mode 100644 index 0000000..c9efb08 --- /dev/null +++ b/src/utils/ITrace.ts @@ -0,0 +1,18 @@ +export interface ITrace { + /** + * Buffer with all trace messages. + * @type {string} + */ + traceBuffer: string; + + /** + * Send message for tracing output. + * @param {string} message Message to format as trace line. + */ + trace(message: string): void; + + /** + * Method to return content of tracing buffer. + */ + returnTrace(): Promise; +} diff --git a/src/utils/slip.ts b/src/utils/slip.ts index 78949f8..568e25c 100644 --- a/src/utils/slip.ts +++ b/src/utils/slip.ts @@ -1,4 +1,5 @@ import { ISerialTransport } from "../transport/ISerialTransport"; +import { ITrace } from "./ITrace"; import { hexConvert } from "./hex"; /** @@ -22,10 +23,11 @@ export interface SlipReaderOutput { /** * Class to handle SLIP read and write serial methods. * @param {ISerialTransport} transport Transport object with raw read and write serial methods - * @param {boolean} enableSlipRead Enable or disable read SLIP data formatting. + * @param {boolean} enableSlipRead Enable or disable read SLIP data formatting. + * @param {ITrace} trace Object that log or trace all serial messages. */ export class Slip { - constructor(private transport: ISerialTransport, public enableSlipRead: boolean = false) {} + constructor(private transport: ISerialTransport, public enableSlipRead: boolean = false, private tracer?: ITrace) {} /** * Format data packet using the Serial Line Internet Protocol (SLIP). @@ -119,17 +121,17 @@ export class Slip { } packet = await this.transport.read(timeout, minData, packet); - if (this.transport.tracing) { - this.transport.trace("Read SLIP bytes"); - this.transport.trace(`Read ${packet.length} bytes: ${hexConvert(packet)}`); + if (this.tracer) { + this.tracer.trace("Read SLIP bytes"); + this.tracer.trace(`Read ${packet.length} bytes: ${hexConvert(packet)}`); } if (this.enableSlipRead) { const slipReaderResult = this.decode(packet); this.transport.leftOver = slipReaderResult.newLeftOver; - if (this.transport.tracing) { - this.transport.trace("Slip reader results"); - this.transport.trace(`Read ${slipReaderResult.packet.length} bytes: ${hexConvert(slipReaderResult.packet)}`); + if (this.tracer) { + this.tracer.trace("Slip reader results"); + this.tracer.trace(`Read ${slipReaderResult.packet.length} bytes: ${hexConvert(slipReaderResult.packet)}`); } return slipReaderResult.packet; }