Skip to content

Commit

Permalink
further refactoring of transport and esploader
Browse files Browse the repository at this point in the history
  • Loading branch information
brianignacio5 committed Jan 31, 2024
1 parent c033caf commit 260e5ea
Show file tree
Hide file tree
Showing 9 changed files with 355 additions and 284 deletions.
275 changes: 113 additions & 162 deletions src/esploader.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { IEspLoaderTerminal, ESPLoader, FlashOptions, LoaderOptions } from "./esploader";
export { classicReset, customReset, hardReset, usbJTAGSerialReset, validateCustomResetStringSequence } from "./reset";
export { ROM } from "./targets/rom";
export { AbstractTransport, ISerialOptions } from "./transport/ITransport";
export { AbstractTransport, ISerialOptions } from "./transport/AbstractTransport";
export { SerialOptions, WebSerialTransport } from "./transport/WebSerialTransport";
2 changes: 1 addition & 1 deletion src/reset.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AbstractTransport } from "./transport/ITransport";
import { AbstractTransport } from "./transport/AbstractTransport";

const DEFAULT_RESET_DELAY = 50;

Expand Down
84 changes: 84 additions & 0 deletions src/transport/AbstractTransport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* Options for device connection.
* @interface ISerialOptions
*/
export interface ISerialOptions {
/**
* A positive, non-zero value indicating the baud rate at which serial communication should be established.
* @type {number}
*/
baudRate: number;
}

/**
* Template to define Transport class which can be consumed by the ESPLoader.
* A Webserial reference implementation is found at src/transport/WebSerialTransport.ts
*/
export abstract class AbstractTransport {
public abstract tracing: boolean;
public abstract slipReaderEnabled: boolean;

/**
* Request the serial device information as string.
* @returns {string} Return the serial device information as formatted string.
*/
public abstract getInfo(): string;

/**
* Request the serial device product id.
* @returns {number | undefined} Return the product ID.
*/
public abstract getPID(): number | undefined;

/**
* Format received or sent data for tracing output.
* @param {string} message Message to format as trace line.
*/
public abstract trace(message: string): void;

/**
* Write binary data to device.
* @param {Uint8Array} data 8 bit unsigned data array to write to device.
*/
public abstract write(data: Uint8Array): Promise<void>;

/**
* Read from serial device.
* @param {number} timeout Read timeout number
* @param {number} minData Minimum packet array length
* @returns {Promise<Uint8Array>} 8 bit unsigned data array read from device.
*/
public abstract read(timeout?: number, minData?: number): Promise<Uint8Array>;

/**
* Read from serial device without formatting.
* @param {number} timeout Read timeout in milliseconds (ms)
* @returns {Promise<Uint8Array>} 8 bit unsigned data array read from device.
*/
public abstract rawRead(timeout?: number): Promise<Uint8Array>;

/**
* Send the RequestToSend (RTS) signal to given state
* # True for EN=LOW, chip in reset and False EN=HIGH, chip out of reset
* @param {boolean} state Boolean state to set the signal
*/
public abstract setRTS(state: boolean): Promise<void>;

/**
* Send the dataTerminalReady (DTS) signal to given state
* # True for IO0=LOW, chip in reset and False IO0=HIGH
* @param {boolean} state Boolean state to set the signal
*/
public abstract setDTR(state: boolean): Promise<void>;

/**
* Connect to serial device using the Webserial open method.
* @param {SerialOptions} serialOptions Serial Options for WebUSB SerialPort class.
*/
public abstract connect(serialOptions: ISerialOptions): Promise<void>;

/**
* Disconnect from serial device by running SerialPort.close() after streams unlock.
*/
public abstract disconnect(): Promise<void>;
}
50 changes: 0 additions & 50 deletions src/transport/ITransport.ts

This file was deleted.

84 changes: 14 additions & 70 deletions src/transport/WebSerialTransport.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/* global SerialPort, ParityType, FlowControlType */

import { AbstractTransport, ISerialOptions } from "./ITransport";
import { AbstractTransport, ISerialOptions } from "./AbstractTransport";
import { hexConvert } from "../utils/hex";
import { appendArray } from "../utils/convert";
import { slipWriter } from "../utils/slip";

/**
* Options for device serialPort.
Expand Down Expand Up @@ -86,6 +89,9 @@ export class WebSerialTransport implements AbstractTransport {
this.traceLog += traceMessage + "\n";
}

/**
* Return the whole trace output to the user clipboard.
*/
async returnTrace() {
try {
await navigator.clipboard.writeText(this.traceLog);
Expand All @@ -95,86 +101,24 @@ export class WebSerialTransport implements AbstractTransport {
}
}

hexify(s: Uint8Array) {
return Array.from(s)
.map((byte) => byte.toString(16).padStart(2, "0"))
.join("")
.padEnd(16, " ");
}

hexConvert(uint8Array: Uint8Array, autoSplit = true) {
if (autoSplit && uint8Array.length > 16) {
let result = "";
let s = uint8Array;

while (s.length > 0) {
const line = s.slice(0, 16);
const asciiLine = String.fromCharCode(...line)
.split("")
.map((c) => (c === " " || (c >= " " && c <= "~" && c !== " ") ? c : "."))
.join("");
s = s.slice(16);
result += `\n ${this.hexify(line.slice(0, 8))} ${this.hexify(line.slice(8))} | ${asciiLine}`;
}

return result;
} else {
return this.hexify(uint8Array);
}
}

/**
* Format data packet using the Serial Line Internet Protocol (SLIP).
* @param {Uint8Array} data Binary unsigned 8 bit array data to format.
* @returns {Uint8Array} Formatted unsigned 8 bit data array.
*/
slipWriter(data: Uint8Array) {
const outData = [];
outData.push(0xc0);
for (let i = 0; i < data.length; i++) {
if (data[i] === 0xdb) {
outData.push(0xdb, 0xdd);
} else if (data[i] === 0xc0) {
outData.push(0xdb, 0xdc);
} else {
outData.push(data[i]);
}
}
outData.push(0xc0);
return new Uint8Array(outData);
}

/**
* Write binary data to device using the WebSerial device writable stream.
* @param {Uint8Array} data 8 bit unsigned data array to write to device.
*/
async write(data: Uint8Array) {
const outData = this.slipWriter(data);
const outData = slipWriter(data);

if (this.device.writable) {
const writer = this.device.writable.getWriter();
if (this.tracing) {
console.log("Write bytes");
this.trace(`Write ${outData.length} bytes: ${this.hexConvert(outData)}`);
this.trace(`Write ${outData.length} bytes: ${hexConvert(outData)}`);
}
await writer.write(outData);
writer.releaseLock();
}
}

/**
* Concatenate buffer2 to buffer1 and return the resulting ArrayBuffer.
* @param {ArrayBuffer} buffer1 First buffer to concatenate.
* @param {ArrayBuffer} buffer2 Second buffer to concatenate.
* @returns {ArrayBuffer} Result Array buffer.
*/
_appendBuffer(buffer1: ArrayBuffer, buffer2: ArrayBuffer) {
const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
tmp.set(new Uint8Array(buffer1), 0);
tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
return tmp.buffer;
}

/**
* Take a data array and return the first well formed packet after
* replacing the escape sequence. Reads at least 8 bytes.
Expand Down Expand Up @@ -260,7 +204,7 @@ export class WebSerialTransport implements AbstractTransport {
this.leftOver = packet;
throw new Error("Timeout");
}
const p = new Uint8Array(this._appendBuffer(packet.buffer, value.buffer));
const p = appendArray(packet, value);
packet = p;
} while (packet.length < minData);
} finally {
Expand All @@ -272,14 +216,14 @@ export class WebSerialTransport implements AbstractTransport {

if (this.tracing) {
console.log("Read bytes");
this.trace(`Read ${packet.length} bytes: ${this.hexConvert(packet)}`);
this.trace(`Read ${packet.length} bytes: ${hexConvert(packet)}`);
}

if (this.slipReaderEnabled) {
const slipReaderResult = this.slipReader(packet);
if (this.tracing) {
console.log("Slip reader results");
this.trace(`Read ${slipReaderResult.length} bytes: ${this.hexConvert(slipReaderResult)}`);
this.trace(`Read ${slipReaderResult.length} bytes: ${hexConvert(slipReaderResult)}`);
}
return slipReaderResult;
}
Expand Down Expand Up @@ -314,7 +258,7 @@ export class WebSerialTransport implements AbstractTransport {
}
if (this.tracing) {
console.log("Raw Read bytes");
this.trace(`Read ${value.length} bytes: ${this.hexConvert(value)}`);
this.trace(`Read ${value.length} bytes: ${hexConvert(value)}`);
}
return value;
} finally {
Expand Down Expand Up @@ -352,7 +296,7 @@ export class WebSerialTransport implements AbstractTransport {

/**
* Connect to serial device using the Webserial open method.
* @param {SerialOptions} serialOptions Serial Options for WebUSB SerialPort class.
* @param {SerialOptions} serialOptions Serial Options for ESP Loader class.
*/
async connect(serialOptions: SerialOptions) {
await this.device.open({
Expand Down
78 changes: 78 additions & 0 deletions src/utils/convert.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* Concatenate array 2 to array 1 and return the resulting UInt8Array.
* @param {Uint8Array} array1 First array to concatenate.
* @param {Uint8Array} array2 Second array to concatenate.
* @returns {Uint8Array} Result UInt8Array.
*/
export function appendArray(array1: Uint8Array, array2: Uint8Array): Uint8Array {
const result = new Uint8Array(array1.length + array2.length);
result.set(array1, 0);
result.set(array2, array1.length);
return result;
}

/**
* Convert short integer to byte array
* @param {number} i - Number to convert.
* @returns {Uint8Array} Byte array.
*/
export function shortToBytearray(i: number): Uint8Array {
return new Uint8Array([i & 0xff, (i >> 8) & 0xff]);
}

/**
* Convert an integer to byte array
* @param {number} i - Number to convert.
* @returns {Uint8Array} Array of byte from interger
*/
export function intToByteArray(i: number): Uint8Array {
return new Uint8Array([i & 0xff, (i >> 8) & 0xff, (i >> 16) & 0xff, (i >> 24) & 0xff]);
}

/**
* Convert a byte array to short integer.
* @param {number} i - Number to convert.
* @param {number} j - Number to convert.
* @returns {number} Return a short integer number.
*/
export function byteArrayToShort(i: number, j: number): number {
return i | (j >> 8);
}

/**
* Convert a byte array to integer.
* @param {number} i - Number to convert.
* @param {number} j - Number to convert.
* @param {number} k - Number to convert.
* @param {number} l - Number to convert.
* @returns {number} Return a integer number.
*/
export function byteArrayToInt(i: number, j: number, k: number, l: number): number {
return i | (j << 8) | (k << 16) | (l << 24);
}

/**
* Convert a unsigned 8 bit integer array to byte string.
* @param {Uint8Array} u8Array - magic hex number to select ROM.
* @returns {string} Return the equivalent string.
*/
export function ui8ToBstr(u8Array: Uint8Array): string {
let bStr = "";
for (let i = 0; i < u8Array.length; i++) {
bStr += String.fromCharCode(u8Array[i]);
}
return bStr;
}

/**
* Convert a byte string to unsigned 8 bit integer array.
* @param {string} bStr - binary string input
* @returns {Uint8Array} Return a 8 bit unsigned integer array.
*/
export function bstrToUi8(bStr: string): Uint8Array {
const u8Array = new Uint8Array(bStr.length);
for (let i = 0; i < bStr.length; i++) {
u8Array[i] = bStr.charCodeAt(i);
}
return u8Array;
}
Loading

0 comments on commit 260e5ea

Please sign in to comment.