serial icon indicating copy to clipboard operation
serial copied to clipboard

Support configuration changes without reopening

Open reillyeon opened this issue 4 years ago • 13 comments

From #71,

@reillyeon Would it be possible to reconsider this reconfigure() method? Most microcontrollers boot up and start listening on a Serial port at an specific baud rate but allow for clients to change the baud rate via commands. For instance in this project https://github.com/esphome/esp-web-tools for Flashing ESP devices over serial, most of the USBtoUART chips on those boards support speeds well above 115200, but once the port is open via Web Serial, and after issuing the command to change the baud rate, there is no way on the client side to also make that change.

A reconfigure() method seems very reasonable. It would probably work similarly to close() in that it requires the readable and writable streams to be closed. It looks like operating systems provide some flexibility around whether data is flushed and drained when the port is reconfigured. For transmitted data the requirement that writable is closed already handles that. For received data I think we can make the decision to only support flushing the read buffers when reconfiguring the port in order to avoid the application encountering corrupted data.

reillyeon avatar Jun 25 '21 01:06 reillyeon

@reillyeon Thank you. So far on the eep-home-webtools we found a partial workaround in the meantime. We close the port and reopen it with the new baud rate. I say partial because there are some USB2Serial controllers commonly used on ESP boards that become irresponsive after closing the port, to that workaround does not work for them.

Speaking of flushing, the Espressiff guys on their flashing tool build with python flush the input buffers after changing the baud rate and after certain operations to get rid of some unneeded data. I believe the current api does not support a flushInput() method on the port, does it? that'd be a nice addition too.

conradopoole avatar Jun 25 '21 16:06 conradopoole

This code will flush the input stream,

let reader = port.readable.getReader();
// Do whatever reading you want. When you want to flush the read buffer,
await reader.cancel();
// port.readable is replaced with a new ReadableStream.
reader = port.readable.getReader();
// Continue reading.

reillyeon avatar Jun 25 '21 17:06 reillyeon

I'm really sad that this important feature still does not exist in the standard.

Port reopening is not a reliable solution, because:

  • We lose time. Closing/opening is not a fast operation.
  • Port signals are reset to default values.
  • A lot of redundant code for handling unexpected reopen of the port.

Azq2 avatar Apr 22 '24 10:04 Azq2

I am also interested in a reconfigure option to be able to properly emulate a DMX signal using a UART driven by the WebSerial interface.

The DMX protocol differs from the traditional RS485 transmission protocol in that it includes a "break" sequence that indicates that a message is about to be sent. Many DMX applications that use serial output emulate this break sequence by changing the baud rate to a much slower rate, broadcasting a 0 byte, then ramping the baud rate back up to the 250,000 baud rate of DMX. This slower baud transmission emulates the brake sequence by the line going low then high for a period.

At the moment, reconnecting the port takes way too long (as noted by @Azq2) and the signals are not close enough temporally for the DMX fixtures to recognize the break sequence.

See this Chromium feature request for more details.

wmmiii avatar May 28 '24 18:05 wmmiii

@wmmiii are you aware that you can send break signals of arbitrary lengths using the setSignals() function (see https://developer.mozilla.org/en-US/docs/Web/API/SerialPort/setSignals)?

Using this function you can set break to true, wait a specified time (e.g. 200ms) then set break to false. I have used this approach with https://ninjaterm.mbedded.ninja/ and it seems to work well.

gbmhunter avatar Jun 16 '24 08:06 gbmhunter

@wmmiii are you aware that you can send break signals of arbitrary lengths using the setSignals() function (see https://developer.mozilla.org/en-US/docs/Web/API/SerialPort/setSignals)?

Using this function you can set break to true, wait a specified time (e.g. 200ms) then set break to false. I have used this approach with https://ninjaterm.mbedded.ninja/ and it seems to work well.

Thanks @gbmhunter! I tried to use this however setSignals cannot be used on an open port. I get the following error when I try to use this right before I write my output:

TypeError: This writable stream writer has been released and cannot be used to monitor the stream's state

I also tried to set the signals before I use the port (right after opening). While it doesn't throw an error my UART does not recognize the signal. I don't have a logic analyzer to see what is going on on the wire though.

Note: Here is how I am setting signals (in both attempts)

await (port as any).setSignals({ break: true });
await new Promise((resolve) => setTimeout(resolve, 200));

// Do the writing here

I tried to search for setSignals in https://github.com/gbmhunter/NinjaTerm to no avail, can you give me an example of how you are using it?

wmmiii avatar Jun 26 '24 04:06 wmmiii

@wmmiii you should be able to use setSignals() with an open port, at least I have been able to with no issues.

I send the break signal in NinjaTerm in App.tsx (using an open port) in the function starting on Line 880 (in the latest version on main), see https://github.com/gbmhunter/NinjaTerm/blob/v4.18.0/src/model/App.tsx#L880 for the example you could look at.

gbmhunter avatar Jun 26 '24 09:06 gbmhunter

+1 to setSignals() should work whenever the port is open. Its operation is independent of the WritableStream.

reillyeon avatar Jun 26 '24 18:06 reillyeon

I would like to also add my support for an addition to the API specification for an ability to change an open port's configuration - particularly the baud rate - without closing and re-opening the port.

My use case would be similar to the originally mentioned use case - for when communicating with microcontrollers that allow to change their baud rate on the fly.

I maintain a web-based ISP flashing tool for WCH RISC-V microcontrollers that uses the Web Serial API to communicate with the microcontroller bootloader. Many of the WCH microcontroller bootloaders support an optional command to change to a faster baud rate. To use it requires first connecting and issuing the "change baud" command at the default baud rate of 115200, then continuing all further communication at the new baud rate.

However, I am unable to currently implement such a feature because closing and re-opening the serial port (at the new baud rate) isn't feasible due to how the DTR/RTS control signals are also reset when doing so, which can be undesirable.

basilhussain avatar May 26 '25 20:05 basilhussain

@reillyeon Any reason why this MOST WANTED feature has been ignored for 4 years? :(

Azq2 avatar Aug 09 '25 14:08 Azq2

For my application, I reverted back to using Electron for better serial control compared to Web Serial. Web Serial is a great idea, but it does not seem like a priority at the moment to add features that would make it on par with native access (it is also lacking adoption in Firefox and Safari). I'm still hoping it improves in the future :-)

gbmhunter avatar Aug 10 '25 05:08 gbmhunter

@reillyeon Any reason why this MOST WANTED feature has been ignored for 4 years? :(

I understand your frustration but the team has been focused on other useful features.

reillyeon avatar Aug 10 '25 07:08 reillyeon

But I think this is not “just a useful feature”, but actually a bug. Without it, WebSerial cannot be considered complete.

Every major OS allows changing the speed without reopening the port, and there’s no need to reset the buffer either.


For those who are frustrated by this:

  1. WebSerial adapter for node-serialport: https://github.com/Azq2/node-serialport-bindings-webserial I’ve implemented the ability to switch speed by reopening the port. This works perfectly fine. It’s also very useful — we can write applications that work both in Node.js and in the browser.

  2. Example application: https://github.com/siemens-mobile-hacks/web-tools SolidJS + node-serialport-bindings-webserial

  3. AsyncSerialPort Example (not library) for async wrapper around node-serialport. Very usefull in 2k25.

    • https://github.com/siemens-mobile-hacks/node-sie-serial/blob/v1.1.10/src/AsyncSerialPort.ts
    • https://github.com/siemens-mobile-hacks/node-sie-serial/blob/v1.1.10/examples/utils.ts#L15-L19

Azq2 avatar Aug 10 '25 09:08 Azq2