Skip to main content




The API serial permite que los sitios Web se comuniquen con dispositivos seriales.


Updated

What is the serial API?

A serial port is a bidirectional communication interface that allows you to send and receive data byte by byte.

La API serial proporciona una forma para que los sitios web lean y escriban en un dispositivo serial con JavaScript. Los dispositivos serie se conectan a través de un puerto serie en el sistema del Username o mediante dispositivos USB y Bluetooth extraíbles que emulan un puerto serie.

In other words, the serial API bridges the web and the physical world by allowing websites to communicate with serial devices, such as microcontrollers and 3D printers.

This API is also a great companion for WebUSB as operating systems require applications to communicate with some serial ports using their native top-level serial API instead of the low-level USB API.

This article reflects the Serial API implemented in Chrome 86 and later. Some property names have changed from previous versions.

Suggested use cases

En los sectores educativo, aficionado e industrial, los usuarios conectan dispositivos periféricos a sus computadoras. Estos dispositivos a menudo son controlados por microcontroladores a través de una conexión en serie utilizada por software personalizado. Algún software personalizado para controlar estos dispositivos está construido con tecnología web:

En algunos casos, los sitios web se comunican con el dispositivo a través de una aplicación de agente nativo que los usuarios instalaron manualmente. En otros, la aplicación se entrega en una aplicación nativa empaquetada a través de un marco como Electron. Y en otros, el usuario debe realizar un paso adicional, como copiar una aplicación compilada al dispositivo a través de una unidad flash USB.

In all these cases, the user experience will be enhanced by providing direct communication between the website and the device it is controlling.

Actual state

Using the serial API

Enable support during the proof of origin phase

The Serial API is available on all desktop platforms (Chrome OS, Linux, macOS, and Windows) as a proof of origin on Chrome 80. The proof of origin is expected to finish just before Chrome 89 is set in February 2021 The API can also be enabled by a flag.

Origin testing allows you to test new features and provide feedback on their usability, practicality, and effectiveness to the web standards community. For more information, see the Origin testing guide for web developers. To enroll in this or any other proof of origin, visit the registration page.

Register for proof of origin

  1. Request a token by your origin.
  2. Add the token to your pages. There are two ways to do it:
    • Add a origin-trial tag to the header of each page. For example, this might look like this:
    • Si puede configurar su server, también puede agregar el token usando un Origin-Trial Encabezado HTTP. El encabezado de respuesta resultante debería verse así:
      Origin-Trial: TOKEN_GOES_HERE

Enabling via chrome: // flags

To experiment with the serial API locally on all desktop platforms, without a source test token, enable the #experimental-web-platform-features flag on
chrome://flags.

Feature detection

To check if the serial API is supported, use:

if ("serial" in navigator) {
}

Open a serial port

La API serial es asincrónica por diseño. Esto evita que la user interface del sitio web se bloquee cuando se espera una entrada, lo cual es importante porque los datos en serie se pueden recibir en cualquier momento, lo que requiere una forma de escucharlos.

To open a serial port, first access a SerialPort object. To do this, you can ask the user to select a single serial port by calling
navigator.serial.requestPort ()or choose one of navigator.serial.getPorts ()
which returns a list of serial ports that the website has previously accessed.


const port = await navigator.serial.requestPort();


const ports = await navigator.serial.getPorts();

the navigator.serial.requestPort () La función toma un literal de objeto opcional que define filters. Se utilizan para hacer coincidir cualquier dispositivo serie conectado a través de USB con un proveedor USB obligatorio (usbVendorId) and optional USB product identifiers (usbProductId).


const filters = [
{ usbVendorId: 0x2341, usbProductId: 0x0043 },
{ usbVendorId: 0x2341, usbProductId: 0x0001 }
];


const port = await navigator.serial.requestPort({ filters });

const { usbProductId, usbVendorId } = port.getInfo();

serial-port-prompt-1927133
User prompt to select a BBC micro: bit

Vocation requestPort () prompts the user to select a device and returns a
SerialPort object. Once you have a SerialPort object calling port.open ()
with the desired baud rate the serial port will open. the baudRate The dictionary member specifies how fast data is sent over a serial line. It is expressed in units of bits per second (bps). Check your device documentation for the correct value as all data you send and receive will be gibberish if this is specified incorrectly. For some USB and Bluetooth devices that emulate a serial port, this value can be safely set to any value as it is ignored by the emulation.


const port = await navigator.serial.requestPort();


await port.open({ baudRate: 9600 });

You can also specify any of the following options when opening a serial port. These options are optional and have predetermined values.

  • dataBits: The number of data bits per frame (7 or 8).
  • stopBits: The number of stop bits at the end of a frame (1 or 2).
  • parity: El modo de paridad (ya be "none", "even" or "odd").
  • bufferSize: The size of the read and write buffers to create (must be less than 16 MB).
  • flowControl: The flow control mode (either "none" or "hardware").

Read from a serial port

The input and output streams in the serial API are handled by the Streams API.

If the streams are new to you, see Streams API concepts. This article just touches the surface of streams and their management.

Once the serial port connection is established, readable and writable
properties of SerialPort object returns a ReadableStream and a
WritableStream. They will be used to receive data and send data to the serial device. They both use Uint8Array instances for data transfer.

When new data arrives from the serial device, port.readable.getReader (). read ()
returns two properties asynchronously: the value and a done boolean. Yes
done it's true, the serial port has been closed or no more data is coming in. Calling port.readable.getReader () create a reader and block readable it. While readable it is locked, the serial port cannot be closed.

const reader = port.readable.getReader();


while (true) {
const { value, done } = await reader.read();
if (done) {
reader.releaseLock();
break;
}
console.log(value);
}

Some non-fatal serial port read errors can occur under some conditions, such as buffer overflow, framing errors, or parity errors. Those are thrown as exceptions and can be caught by adding another loop on top of the previous one that checks port.readable. This works because as long as the errors are not fatal, a new ReadableStream it is created automatically. If a fatal error such as serial device removal occurs, port.readable becomes null.

while (port.readable) {
const reader = port.readable.getReader();

try {
while (true) {
const { value, done } = await reader.read();
if (done) {

reader.releaseLock();
break;
}
if (value) {
console.log(value);
}
}
} catch (error) {

}
}

If the serial device sends text back, you can pipe port.readable through a
TextDecoderStream As shown below. A TextDecoderStream is a transform current
that grabs everything Uint8Array fragments and turns them into strings.

const textDecoder = new TextDecoderStream();
const readableStreamClosed = port.readable.pipeTo(textDecoder.writable);
const reader = textDecoder.readable.getReader();


while (true) {
const { value, done } = await reader.read();
if (done) {

reader.releaseLock();
break;
}

console.log(value);
}

Write to a serial port

To send data to a serial device, pass the data to
port.writable.getWriter (). write (). Vocation releaseLock () in
port.writable.getWriter () it is necessary for the serial port to be closed later.

const writer = port.writable.getWriter();

const data = new Uint8Array([104, 101, 108, 108, 111]);
await writer.write(data);


writer.releaseLock();

Send text to the device through a TextEncoderStream channeled to port.writable
As shown below.

const textEncoder = new TextEncoderStream();
const writableStreamClosed = textEncoder.readable.pipeTo(port.writable);

const writer = textEncoder.writable.getWriter();

await writer.write("hello");

Close a serial port

port.close () close the serial port if your readable and writable The members are unlocked, sense releaseLock () It has been summoned by its respective reader and writer.

await port.close();

However, when continuously reading data from a serial device using a loop,
port.readable it will always be blocked until it encounters an error. In this case, calling reader.cancel () force to reader.read () to solve immediately with {value: undefined, done: true} and thus allowing the loop to call reader.releaseLock ().



const reader = port.readable.getReader();


while (true) {
const { value, done } = await reader.read();
if (done) {
reader.releaseLock();
break;
}

console.log(value);
}



await reader.cancel();
await port.close();

Closing a serial port is more complicated when using it transform streams (I like it
TextDecoderStream and TextEncoderStream). Call reader.cancel () like before. Then call writer.close () and port.close (). This propagates the errors through the transform streams to the underlying serial port. Because error propagation does not happen immediately, you should use the readableStreamClosed and
writableStreamClosed previously created promises to detect when port.readable
and port.writable have been unlocked. Cancel the reader causes the sequence to be aborted; that's why you have to catch and ignore the resulting error.



const textDecoder = new TextDecoderStream();
const readableStreamClosed = port.readable.pipeTo(textDecoder.writable);
const reader = textDecoder.readable.getReader();


while (true) {
const { value, done } = await reader.read();
if (done) {
reader.releaseLock();
break;
}

console.log(value);
}

const textEncoder = new TextEncoderStream();
const writableStreamClosed = textEncoder.readable.pipeTo(port.writable);

reader.cancel();
await readableStreamClosed.catch(() => { });

writer.close();
await writableStreamClosed;

await port.close();

Hear the connection and disconnection

If a USB device provides a serial port, then that device can be connected to or disconnected from the system. When the website has been granted permission to access a serial port, it should monitor the connect and disconnect events.

navigator.serial.addEventListener("connect", (event) => {
});

navigator.serial.addEventListener("disconnect", (event) => {
});

Handle signals

Después de establecer la conexión del puerto serie, puede consultar y configurar explícitamente las señales expuestas por el puerto serie para la detección de dispositivos y el control de flujo. Estas señales se definen como valores booleanos. Por ejemplo, algunos dispositivos como Arduino entrarán en un modo de programming si se activa la señal Data Terminal Ready (DTR).

Adjustment exit signs and get input signals are made respectively by calling port.setSignals () and port.getSignals (). See the usage examples below.


await port.setSignals({ break: false });


await port.setSignals({ dataTerminalReady: true });


await port.setSignals({ requestToSend: false });

const signals = await port.getSignals();
console.log(`Clear To Send: ${signals.clearToSend}`);
console.log(`Data Carrier Detect: ${signals.dataCarrierDetect}`);
console.log(`Data Set Ready: ${signals.dataSetReady}`);
console.log(`Ring Indicator: ${signals.ringIndicator}`);

Transforming streams

When you receive data from the serial device, you will not necessarily get all the data at once. It can be arbitrarily fragmented. For more information, see
Streams API concepts.

To deal with this, you can use some built-in transform streams, like
TextDecoderStream or create your own transformation flow that allows you to analyze the incoming flow and return analyzed data. The transform stream is between the serial device and the read loop that is consuming the stream. You can apply an arbitrary transformation before the data is consumed. Think of it like an assembly line - as a widget moves down the line, each step on the line modifies the widget so that when it reaches its final destination, it is a fully functioning widget.

airplane-factory-2628261
Castle Bromwich WWII Aircraft Factory

For example, consider how to create a transform stream class that consumes a stream and chunks it based on line breaks. Their transform () the method is called each time the flow receives new data. You can queue the data or save it for later. the flush () The method is called when the stream is closed and handles any data that has not yet been processed.

To use the transform stream class, you must pipe an incoming stream through it. In the third code example in Read from a serial port, the original input stream was only piped through a TextDecoderStreamthen we have to call pipeThrough () to channel it through our new LineBreakTransformer.

class LineBreakTransformer {
constructor() {
Este.chunks = "";
}

transform(chunk, controller) {
Este.chunks += chunk;
const lines = Este.chunks.split("rn");
Este.chunks = lines.pop();
lines.forEach((line) => controller.enqueue(line));
}

flush(controller) {
controller.enqueue(Este.chunks);
}
}

const textDecoder = new TextDecoderStream();
const readableStreamClosed = port.readable.pipeTo(textDecoder.writable);
const reader = textDecoder.readable
.pipeThrough(new TransformStream(new LineBreakTransformer()))
.getReader();

To debug serial device communication problems, use the tee() method of
port.readable to split the transmissions going to or from the serial device. The two created flows can be consumed independently and this allows you to print one on the console for inspection.

const [appReadable, devReadable] = port.readable.tee();

Codelab

At Google Developer Code Lab, you will use the serial API to interact with a
BBC micro: bit board to display images on its 5 × 5 LED matrix.

Polyfill

On Android, USB-based serial ports can be supported through the WebUSB API and Polyfill API series. Este polyfill está limitado a hardware y plataformas donde se puede acceder al dispositivo a través de la API WebUSB porque no ha sido reclamado por un controlador de dispositivo integrado.

Security and privacy

The specification authors have designed and implemented the serial API using the basic principles defined in Control access to powerful features of the web platform, including user control, transparency and ergonomics. The ability to use this API is primarily controlled by a permissions model that grants access to only one serial device at a time. In response to a user request, the user must take active steps to select a particular serial device.

To understand the security tradeoffs, see the security and privacy
Serial API Explainer sections.

Feedback

The Chrome team would love to hear your thoughts and experiences with the Serial API.

Tell us about the API design

Is there something in the API that is not working as expected? Or are you missing any methods or properties you need to implement your idea?

File a spec issue on the Serial API GitHub repository or add your thoughts to an existing problem.

Report a problem with the deployment

Found a bug with the Chrome implementation? Or is the implementation different from the specification?

File a bug in https://new.crbug.com. Be sure to include as much detail as you can, provide simple instructions for reproducing the bug, and
Components adjusted to Blink> Serial. Failure works great for quick and easy sharing of reps.

Show support

¿Está planeando utilizar la API serial? Su apoyo público ayuda al equipo de Chrome a priorizar funciones y muestra a otros proveedores de browsers lo importante que es brindarles soporte.

Send a tweet to @Cromodev with the hashtag
#SerialAPI

and let us know where and how you are using it.

Helpful Links

Population:

Thanks

Thanks to Reilly Scholarship and Joe medley for their reviews of this article. Aircraft factory photo by Birmingham Museums Trust in Unsplash.