Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions src/connection/nodejs_serial_connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ class NodeJSSerialConnection extends SerialConnection {

/**
* @param path serial port to connect to, e.g: "/dev/ttyACM0" or "/dev/cu.usbmodem14401"
* @param bootDelayMs ms to wait after the port opens before handshaking. Set to
* ~2500 for boards that reset on connect (DTR -> RST, e.g. Wio-E5/STM32WL
* devboards). Default 0 preserves the original behaviour.
*/
constructor(path) {
constructor(path, bootDelayMs = 0) {
super();
this.serialPortPath = path;
this.bootDelayMs = bootDelayMs;
}

async connect() {
Expand All @@ -23,7 +27,26 @@ class NodeJSSerialConnection extends SerialConnection {
});

this.serialPort.on("open", async () => {
await this.onConnected();

// De-assert DTR/RTS. On boards that wire DTR -> RST (via a cap), opening
// the port asserts DTR and resets the MCU; hold a steady de-asserted state
// to avoid further spurious resets. Harmless on native-USB boards.
try {
this.serialPort.set({ dtr: false, rts: false });
} catch(e) {
// set() unsupported / failed; ignore
}

// Wait for the device to finish booting before handshaking. The MCU was
// reset by the DTR assertion on open; the original immediate onConnected()
// raced that boot (~1.2-1.9 s measured on a Wio-E5) and the handshake was
// lost -> connection timeout. Default bootDelayMs 0 = original behaviour.
if(this.bootDelayMs > 0){
await new Promise((resolve) => setTimeout(resolve, this.bootDelayMs));
}

await this.onConnected();

});

this.serialPort.on("close", () => {
Expand Down
37 changes: 31 additions & 6 deletions src/connection/web_serial_connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import SerialConnection from "./serial_connection.js";

class WebSerialConnection extends SerialConnection {

constructor(serialPort) {
constructor(serialPort, bootDelayMs = 0) {

super();

Expand All @@ -16,14 +16,25 @@ class WebSerialConnection extends SerialConnection {
this.onDisconnected();
});

// fire connected callback after constructor has returned
// fire connected callback after constructor has returned.
//
// bootDelayMs lets boards that reset on connect finish booting before we
// send the handshake. Some boards (e.g. STM32WL / Seeed Wio-E5 devboards)
// wire DTR -> RST through a cap, so opening the port asserts DTR and resets
// the MCU. The original setTimeout(0) raced that boot -> handshake (deviceQuery)
// was sent into a rebooting MCU -> never answered -> connection timeout.
// Measured boot-to-ready on a Wio-E5 repeater: ~1.2-1.9 s, so ~2.5 s is safe.
// Default 0 preserves the original behaviour for native-USB boards.
setTimeout(async () => {
await this.onConnected();
}, 0);
}, bootDelayMs);

}

static async open() {
static async open(options = {}) {

const baudRate = options.baudRate ?? 115200;
const bootDelayMs = options.bootDelayMs ?? 0; // set to ~2500 for DTR-reset boards (Wio-E5)

// ensure browser supports web serial
if(!navigator.serial){
Expand All @@ -38,10 +49,24 @@ class WebSerialConnection extends SerialConnection {

// open port
await serialPort.open({
baudRate: 115200,
baudRate: baudRate,
});

return new WebSerialConnection(serialPort);
// De-assert DTR/RTS after opening.
//
// Chrome's Web Serial asserts DTR on open(); on boards that wire DTR -> RST
// (via a cap) that pulses a reset. We can't suppress the open-time assertion
// (the API gives no pre-open signal control), but holding a steady de-asserted
// state afterwards avoids further spurious resets mid-session. This is harmless
// on native-USB boards (no reset wire). setSignals may be unsupported on some
// platforms, so failures are ignored.
try {
await serialPort.setSignals({ dataTerminalReady: false, requestToSend: false });
} catch(e) {
// setSignals unsupported / failed; ignore
}

return new WebSerialConnection(serialPort, bootDelayMs);

}

Expand Down