1. Introducción
Última actualización: 21/07/2020
Qué compilarás
En este codelab, compilarás una página web que usa la API de Web Serial para interactuar con una placa BBC micro:bit y mostrar imágenes en su matriz LED de 5 x 5. Aprenderás sobre la API de Web Serial y cómo usar flujos legibles, grabables y de transformación para comunicarte con dispositivos seriales a través del navegador.

Qué aprenderás
- Cómo abrir y cerrar un puerto Web Serial
- Cómo usar un bucle de lectura para controlar los datos de un flujo de entrada
- Cómo enviar datos a través de una transmisión de escritura
Requisitos
- Una placa BBC micro:bit con el firmware de Espruino más reciente
- Una versión reciente de Chrome (80 o posterior)
- Conocimiento de HTML, CSS, JavaScript y las Herramientas para desarrolladores de Chrome
Elegimos usar el micro:bit para este codelab porque es económico, ofrece algunas entradas (botones) y salidas (pantalla LED de 5 x 5) y puede proporcionar entradas y salidas adicionales. Consulta la página de BBC micro:bit en el sitio de Espruino para obtener detalles sobre las capacidades de micro:bit.
2. Acerca de la API de Web Serial
La API de Web Serial proporciona una forma para que los sitios web lean y escriban en un dispositivo en serie con secuencias de comandos. La API conecta la Web y el mundo físico, ya que permite que los sitios web se comuniquen con dispositivos seriales, como microcontroladores e impresoras 3D.
Existen muchos ejemplos de software de control creado con tecnología web. Por ejemplo:
En algunos casos, estos sitios web se comunican con el dispositivo a través de una aplicación de agente nativa que el usuario instala de forma manual. En otros casos, la aplicación se entrega en una aplicación nativa empaquetada a través de un framework como Electron. En otros casos, el usuario debe realizar un paso adicional, como copiar una aplicación compilada en el dispositivo con una unidad flash USB.
La experiencia del usuario se puede mejorar proporcionando comunicación directa entre el sitio y el dispositivo que controla.
3. Cómo prepararte
Obtén el código
Todo lo que necesitas para este codelab se encuentra en un proyecto de Glitch.
- Abre una nueva pestaña del navegador y ve a https://web-serial-codelab-start.glitch.me/.
- Haz clic en el vínculo Remix Glitch para crear tu propia versión del proyecto inicial.
- Haz clic en el botón Mostrar y, luego, elige En una ventana nueva para ver tu código en acción.
4. Cómo abrir una conexión serial
Cómo verificar si se admite la API de Web Serial
Lo primero que debes hacer es verificar si la API de Web Serial es compatible con el navegador actual. Para ello, verifica si serial está en navigator.
En el evento DOMContentLoaded, agrega el siguiente código a tu proyecto:
script.js - DOMContentLoaded
// CODELAB: Add feature detection here.
const notSupported = document.getElementById('notSupported');
notSupported.classList.toggle('hidden', 'serial' in navigator);
Esto verifica si se admite Web Serial. Si es así, este código oculta el banner que indica que Web Serial no es compatible.
Probar
- Carga la página.
- Verifica que la página no muestre un banner rojo que indique que Web Serial no es compatible.
Abre el puerto en serie
A continuación, debemos abrir el puerto serie. Al igual que la mayoría de las otras APIs modernas, la API de Web Serial es asíncrona. Esto evita que la IU se bloquee mientras espera la entrada, pero también es importante porque la página web puede recibir datos seriales en cualquier momento, y necesitamos una forma de escucharlos.
Dado que una computadora puede tener varios dispositivos seriales, cuando el navegador intenta solicitar un puerto, le solicita al usuario que elija con qué dispositivo conectarse.
Agrega el siguiente código a tu proyecto:
script.js - connect()
// CODELAB: Add code to request & open port here.
// - Request a port and open a connection.
port = await navigator.serial.requestPort();
// - Wait for the port to open.
await port.open({ baudrate: 9600 });
La llamada a requestPort le solicita al usuario a qué dispositivo se quiere conectar. Llamar a port.open abre el puerto. También debemos proporcionar la velocidad a la que queremos comunicarnos con el dispositivo en serie. El BBC micro:bit usa una conexión de 9,600 baudios entre el chip USB a serie y el procesador principal.
También conectemos el botón de conexión y hagamos que llame a connect() cuando el usuario haga clic en él.
Agrega el siguiente código a tu proyecto:
script.js - clickConnect()
// CODELAB: Add connect code here.
await connect();
Probar
Nuestro proyecto ahora tiene lo mínimo necesario para comenzar. Si se hace clic en el botón Connect, se le solicita al usuario que seleccione el dispositivo serial al que se conectará y, luego, se conecta al micro:bit.
- Vuelve a cargar la página.
- Haz clic en el botón Conectar.
- En el cuadro de diálogo del selector de puertos en serie, selecciona el dispositivo BBC micro:bit y haz clic en Conectar.
- En la pestaña, deberías ver un ícono que indica que te conectaste a un dispositivo serial:

Configura un flujo de entrada para escuchar los datos del puerto en serie
Una vez establecida la conexión, debemos configurar un flujo de entrada y un lector para leer los datos del dispositivo. Primero, llamaremos a port.readable para obtener el flujo legible del puerto. Como sabemos que recibiremos texto del dispositivo, lo canalizaremos a través de un decodificador de texto. A continuación, obtendremos un lector y comenzaremos el bucle de lectura.
Agrega el siguiente código a tu proyecto:
script.js - connect()
// CODELAB: Add code to read the stream here.
let decoder = new TextDecoderStream();
inputDone = port.readable.pipeTo(decoder.writable);
inputStream = decoder.readable;
reader = inputStream.getReader();
readLoop();
El bucle de lectura es una función asíncrona que se ejecuta en un bucle y espera contenido sin bloquear el subproceso principal. Cuando llegan datos nuevos, el lector devuelve dos propiedades: el value y un valor booleano done. Si done es verdadero, el puerto se cerró o no hay más datos entrantes.
Agrega el siguiente código a tu proyecto:
script.js - readLoop()
// CODELAB: Add read loop here.
while (true) {
const { value, done } = await reader.read();
if (value) {
log.textContent += value + '\n';
}
if (done) {
console.log('[readLoop] DONE', done);
reader.releaseLock();
break;
}
}
Probar
Ahora nuestro proyecto puede conectarse al dispositivo y agregará cualquier dato que reciba del dispositivo al elemento de registro.
- Vuelve a cargar la página.
- Haz clic en el botón Conectar.
- En el cuadro de diálogo del selector de puertos en serie, selecciona el dispositivo BBC micro:bit y haz clic en Conectar.
- Deberías ver el logotipo de Espruino:

Cómo configurar un flujo de salida para enviar datos al puerto serie
Por lo general, la comunicación serial es bidireccional. Además de recibir datos del puerto serie, también queremos enviar datos al puerto. Al igual que con el flujo de entrada, solo enviaremos texto a través del flujo de salida al micro:bit.
Primero, crea una transmisión del codificador de texto y canalízala a port.writeable.
script.js - connect()
// CODELAB: Add code setup the output stream here.
const encoder = new TextEncoderStream();
outputDone = encoder.readable.pipeTo(port.writable);
outputStream = encoder.writable;
Cuando se conecta de forma serial con el firmware de Espruino, la placa BBC micro:bit actúa como un bucle de lectura, evaluación e impresión (REPL) de JavaScript, similar a lo que se obtiene en una shell de Node.js. A continuación, debemos proporcionar un método para enviar datos a la transmisión. El siguiente código obtiene un escritor del flujo de salida y, luego, usa write para enviar cada línea. Cada línea que se envía incluye un carácter de salto de línea (\n) para indicarle al micro:bit que evalúe el comando enviado.
script.js - writeToStream()
// CODELAB: Write to output stream
const writer = outputStream.getWriter();
lines.forEach((line) => {
console.log('[SEND]', line);
writer.write(line + '\n');
});
writer.releaseLock();
Para poner el sistema en un estado conocido y evitar que repita los caracteres que le enviamos, debemos enviar un CTRL-C y desactivar la repetición.
script.js - connect()
// CODELAB: Send CTRL-C and turn off echo on REPL
writeToStream('\x03', 'echo(false);');
Probar
Ahora, nuestro proyecto puede enviar y recibir datos del micro:bit. Verifiquemos que podamos enviar un comando correctamente:
- Vuelve a cargar la página.
- Haz clic en el botón Conectar.
- En el cuadro de diálogo del selector de puertos en serie, selecciona el dispositivo BBC micro:bit y haz clic en Conectar.
- Abre la pestaña Console en las Herramientas para desarrolladores de Chrome y escribe
writeToStream('console.log("yes")');.
Deberías ver algo como lo siguiente impreso en la página:

5. Cómo controlar la matriz LED
Crea la cadena de la cuadrícula de la matriz
Para controlar la matriz de LED del micro:bit, debemos llamar a show(). Este método muestra gráficos en la pantalla LED integrada de 5 x 5. Toma un número binario o una cadena.
Iteraremos sobre las casillas de verificación y generaremos un array de 1s y 0s que indicará cuáles están marcadas y cuáles no. Luego, debemos invertir el array, ya que el orden de nuestras casillas de verificación es el opuesto al orden de los LEDs en la matriz. A continuación, convertimos el array en una cadena y creamos el comando para enviarlo al micro:bit.
script.js - sendGrid()
// CODELAB: Generate the grid
const arr = [];
ledCBs.forEach((cb) => {
arr.push(cb.checked === true ? 1 : 0);
});
writeToStream(`show(0b${arr.reverse().join('')})`);
Conecta las casillas de verificación para actualizar la matriz
A continuación, debemos escuchar los cambios en las casillas de verificación y, si cambian, enviar esa información al micro:bit. En el código de detección de funciones (// CODELAB: Add feature detection here.), agrega la siguiente línea:
script.js - DOMContentLoaded
initCheckboxes();
También restableceremos la cuadrícula cuando se conecte el micro:bit por primera vez, de modo que muestre una cara feliz. La función drawGrid() ya está disponible. Esta función funciona de manera similar a sendGrid(); toma un array de 1s y 0s, y marca las casillas de verificación según corresponda.
script.js - clickConnect()
// CODELAB: Reset the grid on connect here.
drawGrid(GRID_HAPPY);
sendGrid();
Probar
Ahora, cuando la página abra una conexión con el micro:bit, enviará una cara feliz. Si haces clic en las casillas de verificación, se actualizará la pantalla en la matriz de LED.
- Vuelve a cargar la página.
- Haz clic en el botón Conectar.
- En el cuadro de diálogo del selector de puertos en serie, selecciona el dispositivo BBC micro:bit y haz clic en Conectar.
- Deberías ver una sonrisa en la matriz de LED de micro:bit.
- Cambia las casillas de verificación para dibujar un patrón diferente en la matriz de LED.
6. Conecta los botones del micro:bit
Agrega un evento de reloj a los botones de micro:bit
El micro:bit tiene dos botones, uno a cada lado de la matriz de LED. Espruino proporciona una función setWatch que envía un evento o una devolución de llamada cuando se presiona el botón. Como queremos escuchar ambos botones, haremos que nuestra función sea genérica y que imprima los detalles del evento.
script.js - watchButton()
// CODELAB: Hook up the micro:bit buttons to print a string.
const cmd = `
setWatch(function(e) {
print('{"button": "${btnId}", "pressed": ' + e.state + '}');
}, ${btnId}, {repeat:true, debounce:20, edge:"both"});
`;
writeToStream(cmd);
A continuación, debemos conectar ambos botones (llamados BTN1 y BTN2 en la placa micro:bit) cada vez que el puerto serie se conecte al dispositivo.
script.js - clickConnect()
// CODELAB: Initialize micro:bit buttons.
watchButton('BTN1');
watchButton('BTN2');
Probar
Además de mostrar una cara feliz cuando se conecta, si presionas cualquiera de los botones del micro:bit, se agregará texto a la página que indica qué botón se presionó. Lo más probable es que cada carácter esté en su propia línea.
- Vuelve a cargar la página.
- Haz clic en el botón Conectar.
- En el cuadro de diálogo del selector de puertos en serie, selecciona el dispositivo BBC micro:bit y haz clic en Conectar.
- Deberías ver una sonrisa en la matriz de LED de micro:bit.
- Presiona los botones del micro:bit y verifica que se agregue texto nuevo a la página con detalles del botón presionado.
7. Usa un flujo de transformación para analizar los datos entrantes
Manejo básico de transmisiones
Cuando se presiona uno de los botones del micro:bit, este envía datos al puerto serie a través de un flujo. Los flujos son muy útiles, pero también pueden ser un desafío porque no necesariamente obtendrás todos los datos de una vez, y es posible que se dividan en fragmentos de forma arbitraria.
Actualmente, la app imprime el flujo entrante a medida que llega (en readLoop). En la mayoría de los casos, cada carácter está en su propia línea, pero eso no es muy útil. Lo ideal es que la transmisión se analice en líneas individuales y que cada mensaje se muestre en su propia línea.
Transforma transmisiones con TransformStream
Para ello, podemos usar una transmisión de transformación ( TransformStream), que permite analizar la transmisión entrante y devolver datos analizados. Un flujo de transformación puede ubicarse entre la fuente del flujo (en este caso, el micro:bit) y lo que sea que consuma el flujo (en este caso, readLoop), y puede aplicar una transformación arbitraria antes de que se consuma finalmente. Piensa en ello como una línea de ensamblaje: a medida que un widget avanza por la línea, cada paso lo modifica, de modo que, cuando llega a su destino final, es un widget completamente funcional.
Para obtener más información, consulta los conceptos de la API de Streams de MDN.
Transforma la transmisión con LineBreakTransformer
Creemos una clase LineBreakTransformer, que tomará una transmisión y la dividirá en fragmentos según los saltos de línea (\r\n). La clase necesita dos métodos, transform y flush. Se llama al método transform cada vez que la transmisión recibe datos nuevos. Puede poner los datos en cola o guardarlos para más tarde. Se llama al método flush cuando se cierra la transmisión, y controla los datos que aún no se procesaron.
En nuestro método transform, agregaremos datos nuevos a container y, luego, verificaremos si hay saltos de línea en container. Si hay, divídela en un array y, luego, itera a través de las líneas llamando a controller.enqueue() para enviar las líneas analizadas.
script.js - LineBreakTransformer.transform()
// CODELAB: Handle incoming chunk
this.container += chunk;
const lines = this.container.split('\r\n');
this.container = lines.pop();
lines.forEach(line => controller.enqueue(line));
Cuando se cierre la transmisión, simplemente vaciaremos los datos restantes del contenedor con enqueue.
script.js - LineBreakTransformer.flush()
// CODELAB: Flush the stream.
controller.enqueue(this.container);
Por último, debemos canalizar el flujo entrante a través del nuevo LineBreakTransformer. Nuestro flujo de entrada original solo se canalizó a través de un TextDecoderStream, por lo que debemos agregar un pipeThrough adicional para canalizarlo a través de nuestro nuevo LineBreakTransformer.
script.js - connect()
// CODELAB: Add code to read the stream here.
let decoder = new TextDecoderStream();
inputDone = port.readable.pipeTo(decoder.writable);
inputStream = decoder.readable
.pipeThrough(new TransformStream(new LineBreakTransformer()));
Probar
Ahora, cuando presiones uno de los botones de micro:bit, los datos impresos deberían devolverse en una sola línea.
- Vuelve a cargar la página.
- Haz clic en el botón Conectar.
- En el cuadro de diálogo del selector de puertos en serie, selecciona el dispositivo BBC micro:bit y haz clic en Conectar.
- Deberías ver una sonrisa en la matriz de LED de micro:bit.
- Presiona los botones del micro:bit y verifica que veas algo similar a lo siguiente:

Transforma la transmisión con JSONTransformer
Podríamos intentar analizar la cadena en JSON en readLoop, pero, en cambio, crearemos un transformador de JSON muy simple que transformará los datos en un objeto JSON. Si los datos no son un JSON válido, simplemente devuelve lo que llegó.
script.js - JSONTransformer.transform
// CODELAB: Attempt to parse JSON content
try {
controller.enqueue(JSON.parse(chunk));
} catch (e) {
controller.enqueue(chunk);
}
A continuación, canaliza el flujo a través de JSONTransformer, después de que haya pasado por LineBreakTransformer. Esto nos permite mantener nuestro JSONTransformer simple, ya que sabemos que el JSON solo se enviará en una sola línea.
script.js - connect
// CODELAB: Add code to read the stream here.
let decoder = new TextDecoderStream();
inputDone = port.readable.pipeTo(decoder.writable);
inputStream = decoder.readable
.pipeThrough(new TransformStream(new LineBreakTransformer()))
.pipeThrough(new TransformStream(new JSONTransformer()));
Probar
Ahora, cuando presiones uno de los botones del micro:bit, deberías ver [object Object] impreso en la página.
- Vuelve a cargar la página.
- Haz clic en el botón Conectar.
- En el cuadro de diálogo del selector de puertos en serie, selecciona el dispositivo BBC micro:bit y haz clic en Conectar.
- Deberías ver una sonrisa en la matriz de LED de micro:bit.
- Presiona los botones del micro:bit y verifica que veas algo similar a lo siguiente:
Cómo responder a las pulsaciones de los botones
Para responder a las pulsaciones de los botones de micro:bit, actualiza readLoop para verificar si los datos que recibió son un object con una propiedad button. Luego, llama a buttonPushed para controlar la presión del botón.
script.js - readLoop()
const { value, done } = await reader.read();
if (value && value.button) {
buttonPushed(value);
} else {
log.textContent += value + '\n';
}
Cuando se presiona un botón de micro:bit, debería cambiar la pantalla de la matriz LED. Usa el siguiente código para establecer la matriz:
script.js - buttonPushed()
// CODELAB: micro:bit button press handler
if (butEvt.button === 'BTN1') {
divLeftBut.classList.toggle('pressed', butEvt.pressed);
if (butEvt.pressed) {
drawGrid(GRID_HAPPY);
sendGrid();
}
return;
}
if (butEvt.button === 'BTN2') {
divRightBut.classList.toggle('pressed', butEvt.pressed);
if (butEvt.pressed) {
drawGrid(GRID_SAD);
sendGrid();
}
}
Probar
Ahora, cuando presiones uno de los botones del micro:bit, la matriz LED debería cambiar a una cara feliz o triste.
- Vuelve a cargar la página.
- Haz clic en el botón Conectar.
- En el cuadro de diálogo del selector de puertos en serie, selecciona el dispositivo BBC micro:bit y haz clic en Conectar.
- Deberías ver una sonrisa en la matriz de LED de micro:bit.
- Presiona los botones del micro:bit y verifica que cambie la matriz de LED.
8. Cómo cerrar el puerto en serie
El último paso es conectar la funcionalidad de desconexión para cerrar el puerto cuando el usuario termine.
Cierra el puerto cuando el usuario haga clic en el botón Conectar/Desconectar
Cuando el usuario hace clic en el botón Conectar/Desconectar, debemos cerrar la conexión. Si el puerto ya está abierto, llama a disconnect() y actualiza la IU para indicar que la página ya no está conectada al dispositivo serial.
script.js - clickConnect()
// CODELAB: Add disconnect code here.
if (port) {
await disconnect();
toggleUIConnected(false);
return;
}
Cierra los flujos y el puerto
En la función disconnect, debemos cerrar el flujo de entrada, el flujo de salida y el puerto. Para cerrar el flujo de entrada, llama a reader.cancel(). La llamada a cancel es asíncrona, por lo que debemos usar await para esperar a que se complete:
script.js - disconnect()
// CODELAB: Close the input stream (reader).
if (reader) {
await reader.cancel();
await inputDone.catch(() => {});
reader = null;
inputDone = null;
}
Para cerrar el flujo de salida, obtén un writer, llama a close() y espera a que se cierre el objeto outputDone:
script.js - disconnect()
// CODELAB: Close the output stream.
if (outputStream) {
await outputStream.getWriter().close();
await outputDone;
outputStream = null;
outputDone = null;
}
Por último, cierra el puerto en serie y espera a que se cierre:
script.js - disconnect()
// CODELAB: Close the port.
await port.close();
port = null;
Probar
Ahora puedes abrir y cerrar el puerto serie cuando quieras.
- Vuelve a cargar la página.
- Haz clic en el botón Conectar.
- En el cuadro de diálogo del selector de puertos en serie, selecciona el dispositivo BBC micro:bit y haz clic en Connect.
- Deberías ver una sonrisa en la matriz de LED del micro:bit.
- Presiona el botón Desconectar y verifica que la matriz de LED se apague y que no haya errores en la consola.
9. Felicitaciones
¡Felicitaciones! Creaste correctamente tu primera app web que usa la API de Web Serial.
Mantente al tanto de https://goo.gle/fugu-api-tracker para conocer las novedades de la API de Web Serial y todas las demás capacidades web nuevas y emocionantes en las que está trabajando el equipo de Chrome.