Última actualización: 8/11/2019
Qué compilarás
En este codelab, crearás una página web que usa la API de Web Serial para interactuar con una placa BBC de microbits a fin de mostrar imágenes en su matriz LED de 5 x 5. Aprenderás sobre la API de Web Serial y cómo usar transmisiones legibles, de escritura y de transformación para comunicarte con dispositivos en serie a través del navegador.
Qué aprenderás
- Cómo abrir y cerrar un puerto en serie web
- Cómo usar un bucle de lectura para manejar datos de una transmisión de entrada
- Cómo enviar datos mediante una transmisión de escritura
Requisitos
- Una placa de BBC de micro-bit con el figura de firmware Espruino más reciente
- Una versión reciente de Chrome (78 o posterior)
- Conocimiento de HTML, CSS, JavaScript y Herramientas para desarrolladores de Chrome
Para este codelab, elegimos usar la micro:bit porque es asequible, ofrece algunas entradas (botones) y salidas (pantalla LED de 5x5), y puede proporcionar entradas y salidas adicionales. Consulta la página de microbits de BBC en el sitio de Espruino para obtener detalles sobre las funciones de micro-bit.
La API de Web Serial proporciona a los sitios web una forma de leer y escribir en un dispositivo serial con secuencias de comandos. La API une la Web y el mundo físico, ya que permite que los sitios web se comuniquen con dispositivos en serie, como microcontroladores e impresoras 3D.
Hay muchos ejemplos de software de control que se compila 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 nativo que el usuario instala manualmente. En otros casos, la aplicación se entrega en una aplicación nativa empaquetada a través de un marco de trabajo, como Electron. En otros casos, se requiere que el usuario realice un paso adicional, como copiar una aplicación compilada al dispositivo con una unidad de memoria flash USB.
Para mejorar la experiencia del usuario, proporcione una comunicación directa entre el sitio y el dispositivo que controla.
Habilita la API de Web Serial
La API de Web Serial está en desarrollo y solo está disponible con una marca. Debes habilitar la marca #enable-experimental-web-platform-features
en chrome://flags
.
Obtén el código
Incorporamos en un proyecto de Glitch todo lo que necesitas para este codelab.
- Abra una nueva pestaña del navegador y vaya a https://web-serial-codelab-start.glitch.me/.
- Haz clic en el vínculo de Remix Glitch para crear tu propia versión del proyecto inicial.
- Haga clic en el botón Show y, luego, elija In a New Window para ver el código en acción.
Comprueba si se admite la API de Web Serial
Lo primero que debes hacer es comprobar 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.
if ('serial' in navigator) {
const notSupported = document.getElementById('notSupported');
notSupported.classList.add('hidden');
}
De esta manera, se verifica si Serial Web es compatible. De ser así, este código oculta el banner que indica que no se admite Web Serial.
Pruébalo
- Carga la página.
- Verifica que la página no muestre un banner rojo que indique que no se admite el número de serie web.
Abra el puerto en serie
A continuación, debemos abrir el puerto en serie. Al igual que la mayoría de las API modernas, la API de Web Serial es asíncrona. Esto evita que la IU se bloquee cuando se espera la entrada, pero también es importante porque los datos de serie pueden recibirse en la página web en cualquier momento y necesitamos una manera 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 quiere 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 requestPort
solicita al usuario a qué dispositivo desea conectarse. Llamar al port.open
abre el puerto. También debemos proporcionar la velocidad a la que queremos comunicarnos con el dispositivo en serie. La micro-bit de la BBC utiliza una conexión en 9600 baud entre el chip USB a serie y el procesador principal.
También conectaremos el botón de conexión y haremos 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();
Pruébalo
Para comenzar, nuestro proyecto ya tiene el mínimo. Cuando se hace clic en el botón Conectar, se le solicita al usuario que seleccione el dispositivo en serie al que desea conectarse y, luego, se conectará al micro:bit.
- Vuelve a cargar la página.
- Haz clic en el botón Conectar.
- En el diálogo del selector del puerto en serie, selecciona el dispositivo de la micro-bit de la BBC y haz clic en Conectar.
- En la pestaña, deberías ver un ícono que indica que te conectaste a un dispositivo en serie:
Cómo configurar una transmisión 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, obtendremos una transmisión legible desde el puerto llamando a port.readable
. 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 el contenido sin bloquear el subproceso principal. Cuando llegan nuevos datos, el lector muestra dos propiedades: la booleana value
y la booleana done
. Si done
es verdadero, el puerto se cerró o no llegan más datos.
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;
}
}
Pruébalo
Nuestro proyecto ahora puede conectarse al dispositivo y adjuntar los datos recibidos del dispositivo al elemento de registro.
- Vuelve a cargar la página.
- Haz clic en el botón Conectar.
- En el diálogo del selector del puerto en serie, selecciona el dispositivo de la micro-bit de la BBC y haz clic en Conectar.
- Debería ver el logotipo de Espruino:
Cómo configurar una transmisión de salida para enviar datos al puerto en serie
La comunicación en serie suele ser bidireccional. Además de recibir datos desde el puerto en serie, también queremos enviar datos al puerto. Al igual que con la transmisión de entrada, solo enviaremos texto a través de la transmisión de salida a micro:bit.
Primero, crea una transmisión de codificador de texto y canaliza la transmisión 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 a través de una serie con el firmware de Espruino, la placa micro-bit de la BBC actúa como un bucle de lectura-evaluación (REPL) de JavaScript, similar a lo que se obtiene en un shell de Node.js. A continuación, debemos proporcionar un método para enviar datos al flujo. 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 línea nueva (\n
) para indicar al micro:bit que debe evaluar 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 colocar el sistema en un estado conocido y evitar que se repitan los caracteres que le enviamos, debemos enviar una tecla CTRL-C y desactivar el eco.
script.js - connect()
// CODELAB: Send CTRL-C and turn off echo on REPL
writeToStream('\x03', 'echo(false);');
Pruébalo
Nuestro proyecto ahora puede enviar y recibir datos desde una microbit. Verifiquemos que podemos enviar correctamente un comando:
- Vuelve a cargar la página.
- Haz clic en el botón Conectar.
- En el diálogo del selector del puerto en serie, selecciona el dispositivo de la micro-bit de la BBC y haz clic en Conectar.
- Abre la pestaña Console en Chrome DevTools y escribe
writeToStream('console.log("yes")');
Deberías ver algo como esto impreso en la página:
Compila la string de cuadrícula de la matriz
Para controlar la matriz de LED en micro-bit, debemos llamar a show()
. Este método muestra gráficos en la pantalla LED integrada de 5 x 5. Esto requiere un número binario o una string.
Iterarás las casillas de verificación y generarás un arreglo de 1 y 0, que indica cuál está marcada y cuál no. Luego, tenemos que invertir el array, ya que el orden de las casillas de verificación es opuesto al orden de los LED de la matriz. A continuación, convertimos el arreglo en una string y creamos el comando para enviarlo a 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, tenemos que 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 vamos a restablecer la cuadrícula cuando el micro:bit se conecte por primera vez para que muestre una cara feliz. Ya se proporcionó la función drawGrid()
. Esta función es similar a sendGrid()
, ya que toma un arreglo de 1 y 0, y marca las casillas de verificación según corresponda.
script.js - clickConnect()
// CODELAB: Reset the grid on connect here.
drawGrid(GRID_HAPPY);
sendGrid();
Pruébalo
Ahora, cuando la página abra una conexión al micro:bit, se mostrará una cara feliz. Si haces clic en las casillas de verificación, se actualizará la pantalla de la matriz de LED.
- Vuelve a cargar la página.
- Haz clic en el botón Conectar.
- En el diálogo del selector del puerto en serie, selecciona el dispositivo de la micro-bit de la BBC y haz clic en Conectar.
- Deberías ver una sonrisa en la matriz de micro-bit LED.
- Dibuja un patrón diferente en la matriz de LED cambiando las casillas de verificación.
Cómo agregar un evento de reloj en los botones micro:bit
Hay dos botones en micro-bit, uno a cada lado de la matriz LED. Espruino brinda 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 la función sea genérica y que se impriman 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, necesitamos conectar ambos botones (llamados BTN1 y BTN2 en la placa micro:bit) cada vez que el puerto en serie se conecte al dispositivo.
script.js - clickConnect()
// CODELAB: Initialize micro:bit buttons.
watchButton('BTN1');
watchButton('BTN2');
Pruébalo
Además de mostrar una cara feliz cuando esté conectado, al presionar cualquiera de los botones de la micro-bit se agregará texto a la página para indicar el botón que se presionó. Lo más probable es que cada personaje esté en su propia línea.
- Vuelve a cargar la página.
- Haz clic en el botón Conectar.
- En el diálogo del selector del puerto en serie, selecciona el dispositivo de la micro-bit de la BBC y haz clic en Conectar.
- Deberías ver una sonrisa en la matriz de LED de micro-bits.
- Presiona los botones en la micro-bit y verifica que agregue nuevo texto a la página con los detalles del botón presionado.
Manejo básico de transmisiones
Cuando se presiona uno de los botones de micro-bit, el micro-bit envía datos al puerto en serie mediante una transmisión. Las transmisiones son muy útiles, pero también pueden ser un desafío porque no será necesario obtener todos los datos a la vez, sino que pueden fragmentarse de forma arbitraria.
Actualmente, la app imprime la transmisión entrante a medida que llega (en readLoop
). En la mayoría de los casos, cada personaje utiliza su propia línea, pero eso no es muy útil. Lo ideal sería que la transmisión se analizara en líneas individuales y que cada mensaje se mostrara como 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 mostrar los datos analizados. Una transmisión de transformación se puede ubicar entre la fuente de transmisión (en este caso, la micro:bit) y lo que consuma la transmisión (en este caso, readLoop
) y se puede aplicar una transformación arbitraria antes de que se consuma. Considéralo una línea de ensamblado: a medida que un widget baja por la línea, cada paso de la línea modifica el widget para que, cuando llegue a su destino final, sea un widget que funcione correctamente.
Para obtener más información, consulta los Conceptos de la API de flujos de MDN.
Transforma la transmisión con LineBreakTransformer
Crearemos una clase LineBreakTransformer
, que tomará una transmisión y la fragmentará en función de 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 usarlos más adelante. Se llama al método flush
cuando se cierra la transmisión, y esta controla los datos que todavía no se procesaron.
En nuestro método transform
, agregaremos datos nuevos a container
y, luego, comprobaremos si hay saltos de línea en container
. Si existen, divídelas en un array. Luego, iteran entre las líneas y llamen 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 cierra la transmisión, solo limpiamos los datos restantes en el contenedor con enqueue
.
script.js - LineBreakTransformer.flush()
// CODELAB: Flush the stream.
controller.enqueue(this.container);
Por último, debemos canalizar la transmisión entrante a través de la nueva LineBreakTransformer
. Nuestra transmisión de entrada original solo se canalizó a través de un TextDecoderStream
, por lo que debemos agregar una pipeThrough
adicional a fin de canalizarla 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()));
Pruébalo
Ahora, cuando presiones uno de los botones de micro-bit, los datos impresos deben mostrarse en una sola línea.
- Vuelve a cargar la página.
- Haz clic en el botón Conectar.
- En el diálogo del selector del puerto en serie, selecciona el dispositivo de la micro-bit de la BBC y haz clic en Conectar.
- Deberías ver una sonrisa en la matriz de micro-bit LED.
- Presiona los botones en la micro-bit y verifica que veas algo similar a lo siguiente:
Transforma la transmisión con JSONTransformer
Podemos intentar analizar la string en JSON en readLoop
, pero, en su lugar, crearemos un transformador JSON muy simple que transformará los datos en un objeto JSON. Si los datos no son JSON válidos, simplemente muestra lo que ingresó.
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 la transmisión a través del JSONTransformer
, después de que haya pasado por la 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()));
Pruébalo
Ahora, cuando presiones uno de los botones de 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 diálogo del selector del puerto en serie, selecciona el dispositivo de la micro-bit de la BBC y haz clic en Conectar.
- Deberías ver una sonrisa en la matriz de micro-bit LED.
- Presiona los botones de la micro-bit y verifica que puedas ver algo como lo siguiente:
Cómo responder a las pulsaciones de los botones
A fin de responder a las pulsaciones del botón micro:bit, actualiza readLoop
para comprobar si los datos que recibió son un object
con una propiedad button
. Luego, llama a buttonPushed
para controlar la pulsació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, la pantalla de la matriz de LED debe cambiar. Usa el siguiente código para configurar 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();
}
}
Pruébalo
Ahora, cuando presiones uno de los botones de micro-bit, la matriz de LED debería cambiar a un rostro feliz o uno triste.
- Vuelve a cargar la página.
- Haz clic en el botón Conectar.
- En el diálogo del selector del puerto en serie, selecciona el dispositivo de la micro-bit de la BBC y haz clic en Conectar.
- Deberías ver una sonrisa en la matriz de LED de micro-bits.
- Presiona los botones en micro-bit y verifica que la matriz de LED cambie.
El paso final es conectar la funcionalidad de desconexión para cerrar el puerto cuando el usuario termine.
Cierra el puerto cuando el usuario hace clic en el botón Conectar/Desconectar
Cuando el usuario hace clic en el botón Conectar o 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 en serie.
script.js - clickConnect()
// CODELAB: Add disconnect code here.
if (port) {
await disconnect();
toggleUIConnected(false);
return;
}
Cierre las transmisiones y el puerto
En la función disconnect
, debemos cerrar la transmisión de entrada, cerrar la transmisión de salida y cerrar 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;
reader = null;
inputDone = null;
}
Para cerrar la transmisión 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;
Pruébalo
Ahora, puedes abrir y cerrar el puerto en serie cuando lo desees.
- Vuelve a cargar la página.
- Haz clic en el botón Conectar.
- En el diálogo del selector del puerto en serie, selecciona el dispositivo de micro-bit de la BBC y haz clic en Conectar.
- Deberías ver una sonrisa en la matriz de micro-bit LED
- Presiona el botón Desconectar y verifica que la matriz de LED se apague y no haya errores en la consola.
¡Felicitaciones! Creaste correctamente tu primera aplicación web que usa la API de Web Serial.
Mantente atento a https://goo.gle/fugu-api-tracker para conocer las novedades de la API de Web Serial y todas las demás emocionantes funciones web en las que está trabajando el equipo de Chrome.