Introducción a la API de serie web

Última actualización: 2020-07-21

Lo que vas a construir

En este laboratorio de código, creará una página web que utiliza la API Web Serial para interactuar con una placa BBC micro: bit para mostrar imágenes en su matriz de LED de 5x5. Aprenderá acerca de la API Web Serial y cómo usar transmisiones legibles, grabables y transformadas para comunicarse con dispositivos seriales a través del navegador.

81167ab7c01d353d.png

Lo que aprenderás

  • Cómo abrir y cerrar un puerto serie web
  • Cómo usar un bucle de lectura para manejar datos de un flujo de entrada
  • Cómo enviar datos a través de una secuencia de escritura

Lo que necesitarás

Elegimos usar el micro: bit para este codelab porque es asequible, ofrece algunas entradas (botones) y salidas (pantalla LED de 5x5) y puede proporcionar entradas y salidas adicionales. Consulte la página de BBC micro: bit en el sitio de Espruino para obtener detalles sobre lo que es capaz de hacer micro: bit.

La API de serie web proporciona una forma para que los sitios web lean y escriban en un dispositivo serie con scripts. La API une la web y el mundo físico al permitir que los sitios web se comuniquen con dispositivos en serie, como microcontroladores e impresoras 3D.

Hay muchos ejemplos de software de control que se construye utilizando 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 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 una comunicación directa entre el sitio y el dispositivo que está controlando.

Obtener el codigo

Hemos puesto todo lo que necesita para este codelab en un proyecto Glitch.

  1. Abra una nueva pestaña del navegador y vaya a https://web-serial-codelab-start.glitch.me/ .
  2. Haga clic en el enlace Remix Glitch para crear su propia versión del proyecto inicial.
  3. Haga clic en el botón Mostrar y luego elija En una nueva ventana para ver su código en acción.

Compruebe si la API de serie web es compatible

Lo primero que debe hacer es verificar si la API de serie web es compatible con el navegador actual. Para hacer eso, verifique si el serial está en el navigator .

En el evento DOMContentLoaded , agregue el siguiente código a su proyecto:

script.js - DOMContentLoaded

// CODELAB: Add feature detection here.
const notSupported = document.getElementById('notSupported');
notSupported.classList.toggle('hidden', 'serial' in navigator);

Esto comprueba si se admite Web Serial. Si es así, este código oculta el banner que dice que Web Serial no es compatible.

Intentalo

  1. Cargue la página.
  2. Verifique que la página no muestre una pancarta roja que indique que la Serie Web no es compatible.

Abra el puerto serial

A continuación, necesitamos abrir el puerto serie. Como la mayoría de las otras API modernas, la API de serie web es asincrónica. Esto evita que la interfaz de usuario se bloquee cuando se espera una entrada, pero también es importante porque la página web puede recibir datos en serie en cualquier momento y necesitamos una forma de escucharlos.

Debido a que una computadora puede tener varios dispositivos en serie, cuando el navegador intenta solicitar un puerto, le pide 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 requestPort pregunta al usuario a qué dispositivo desea conectarse. Llamar a port.open abre el puerto. También necesitamos proporcionar la velocidad a la que queremos comunicarnos con el dispositivo serie. El BBC micro: bit utiliza una conexión de 9600 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();

Intentalo

Nuestro proyecto ahora tiene lo mínimo para comenzar. Al hacer clic en el botón Conectar, se le pide al usuario que seleccione el dispositivo serie al que conectarse y luego se conecta al micro: bit.

  1. Vuelve a cargar la página.
  2. Haga clic en el botón Conectar .
  3. En el cuadro de diálogo del selector de puerto serie, seleccione el dispositivo BBC micro: bit y haga clic en Conectar .
  4. En la pestaña, debería ver un icono que indica que se ha conectado a un dispositivo serie:

d9d0d3966960aeab.png

Configure un flujo de entrada para escuchar datos desde el puerto serie

Una vez establecida la conexión, debemos configurar un flujo de entrada y un lector para leer los datos del dispositivo. Primero, obtendremos el flujo legible del puerto llamando a port.readable . Como sabemos que obtendremos texto del dispositivo, lo canalizaremos a través de un decodificador de texto. A continuación, obtendremos un lector e iniciaremos el ciclo 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 ciclo de lectura es una función asincrónica que se ejecuta en un ciclo y espera el contenido sin bloquear el hilo principal. Cuando llegan nuevos datos, el lector devuelve dos propiedades: el value y un booleano done . Si lo done es cierto, el puerto se ha cerrado o no ingresan 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;
  }
}

Intentalo

Nuestro proyecto ahora puede conectarse al dispositivo y agregará cualquier dato recibido del dispositivo al elemento de registro.

  1. Vuelve a cargar la página.
  2. Haga clic en el botón Conectar .
  3. En el cuadro de diálogo del selector de puerto serie, seleccione el dispositivo BBC micro: bit y haga clic en Conectar .
  4. Debería ver el logo de Espruino:

93494fd58ea835eb.png

Configure un flujo de salida para enviar datos al puerto serie

La comunicación en serie suele ser 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, cree un flujo de codificador de texto y port.writeable el flujo 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 en serie con el firmware de Espruino, la placa BBC micro: bit actúa como un bucle de lectura-evaluación-impresión de JavaScript (REPL) , similar a lo que se obtiene en un 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 nueva línea ( \n ), para decirle 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, necesitamos enviar un CTRL-C y apagar el eco.

script.js - connect()

// CODELAB: Send CTRL-C and turn off echo on REPL
writeToStream('\x03', 'echo(false);');

Intentalo

Nuestro proyecto ahora puede enviar y recibir datos desde micro: bit. Verifiquemos que podemos enviar correctamente un comando:

  1. Vuelve a cargar la página.
  2. Haga clic en el botón Conectar .
  3. En el cuadro de diálogo del selector de puerto serie, seleccione el dispositivo BBC micro: bit y haga clic en Conectar .
  4. Abra la pestaña Consola en Chrome DevTools y escriba writeToStream('console.log("yes")');

Debería ver algo como esto impreso en la página:

a13187e7e6260f7f.png

Construye la cadena de la cuadrícula matricial

Para controlar la matriz de LED en el micro: bit, necesitamos llamar a show() . Este método muestra gráficos en la pantalla LED integrada de 5x5. Esto toma un número binario o una cadena.

Iteraremos sobre las casillas de verificación y generaremos una matriz de 1 y 0 que indique cuál está marcada y cuál no. Luego necesitamos invertir la matriz, porque el orden de nuestras casillas de verificación es el opuesto al orden de los LED en la matriz. A continuación, convertimos la matriz 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 características ( // CODELAB: Add feature detection here. ), agregue la siguiente línea:

script.js - DOMContentLoaded

initCheckboxes();

También restablezcamos la cuadrícula cuando el micro: bit se conecte por primera vez, para que muestre una cara feliz. La función drawGrid() ya se proporciona. Esta función funciona de manera similar a sendGrid() ; toma una matriz 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();

Intentalo

Ahora, cuando la página abra una conexión al micro: bit, enviará una cara feliz. Al hacer clic en las casillas de verificación, se actualizará la pantalla en la matriz de LED.

  1. Vuelve a cargar la página.
  2. Haga clic en el botón Conectar .
  3. En el cuadro de diálogo del selector de puerto serie, seleccione el dispositivo BBC micro: bit y haga clic en Conectar .
  4. Debería ver una sonrisa en la matriz de LED de micro: bits.
  5. Dibuje un patrón diferente en la matriz de LED cambiando las casillas de verificación.

Agregar un evento de reloj en los botones micro: bit

Hay dos botones en el micro: bit, uno a cada lado de la matriz de LED. Espruino proporciona una función setWatch que envía un evento / devolución de llamada cuando se presiona el botón. Como queremos escuchar ambos botones, haremos nuestra función genérica y haremos 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, necesitamos conectar ambos botones (llamados BTN1 y BTN2 en la placa micro: bit) cada vez que el puerto serie está conectado al dispositivo.

script.js - clickConnect()

// CODELAB: Initialize micro:bit buttons.
watchButton('BTN1');
watchButton('BTN2');

Intentalo

Además de mostrar una cara feliz cuando está conectado, al presionar 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 personaje esté en su propia línea.

  1. Vuelve a cargar la página.
  2. Haga clic en el botón Conectar .
  3. En el cuadro de diálogo del selector de puerto serie, seleccione el dispositivo BBC micro: bit y haga clic en Conectar .
  4. Debería ver una sonrisa en la matriz de LED de micro: bits.
  5. Presione los botones del micro: bit y verifique que agregue texto nuevo a la página con los detalles del botón presionado.

Manejo de flujo básico

Cuando se presiona uno de los botones micro: bit, el micro: bit envía datos al puerto serie a través de un flujo. Las transmisiones son muy útiles, pero también pueden ser un desafío porque no necesariamente obtendrá todos los datos a la vez, y pueden estar divididos arbitrariamente.

La aplicación actualmente imprime la transmisión entrante a medida que llega (en readLoop ). En la mayoría de los casos, cada personaje está en su propia línea, pero eso no es muy útil. Idealmente, la secuencia debe analizarse en líneas individuales y cada mensaje debe mostrarse como su propia línea.

Transformar transmisiones con TransformStream

Para hacer eso, podemos usar un flujo de transformación ( TransformStream ), que hace posible analizar el flujo 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 esté consumiendo el flujo (en este caso readLoop ), y puede aplicar una transformación arbitraria antes de que finalmente se consuma. Piense en ello como una línea de montaje: a medida que un widget desciende por la línea, cada paso de la línea modifica el widget, de modo que cuando llega a su destino final, es un widget en pleno funcionamiento.

Para obtener más información, consulte los conceptos de la API Streams de MDN .

Transforma la transmisión con LineBreakTransformer

LineBreakTransformer una clase LineBreakTransformer , que tomará una secuencia y la dividirá en función de los saltos de línea ( \r\n ). La clase necesita dos métodos, transform y flush . El método de transform se llama cada vez que el flujo recibe nuevos datos. Puede poner los datos en cola o guardarlos para más adelante. El método flush se llama cuando se cierra la secuencia y maneja cualquier dato que aún no se haya procesado.

En nuestro método de transform , agregaremos nuevos datos al container y luego verificaremos si hay saltos de línea en el container . Si los hay, divídalo en una matriz y luego itere 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 la secuencia se cierra, simplemente eliminaremos cualquier dato restante en el contenedor usando enqueue .

script.js - LineBreakTransformer.flush()

// CODELAB: Flush the stream.
controller.enqueue(this.container);

Finalmente, necesitamos canalizar el flujo entrante a través del nuevo LineBreakTransformer . Nuestro flujo de entrada original solo se canalizó a través de TextDecoderStream , por lo que necesitamos 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()));

Intentalo

Ahora, cuando presiona uno de los botones micro: bit, los datos impresos deben devolverse en una sola línea.

  1. Vuelve a cargar la página.
  2. Haga clic en el botón Conectar .
  3. En el cuadro de diálogo del selector de puerto serie, seleccione el dispositivo BBC micro: bit y haga clic en Conectar .
  4. Debería ver una sonrisa en la matriz de LED de micro: bits.
  5. Presione los botones del micro: bit y verifique que ve algo como lo siguiente:

6c2193880c748412.png

Transforma la transmisión con JSONTransformer

Podríamos intentar analizar la cadena en JSON en readLoop , pero en su lugar, readLoop un transformador JSON muy simple que transformará los datos en un objeto JSON. Si los datos no son JSON válidos, simplemente devuelva 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, JSONTransformer la transmisión 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()));

Intentalo

Ahora, cuando presione uno de los botones micro: bit, debería ver [object Object] impreso en la página.

  1. Vuelve a cargar la página.
  2. Haga clic en el botón Conectar .
  3. En el cuadro de diálogo del selector de puerto serie, seleccione el dispositivo BBC micro: bit y haga clic en Conectar .
  4. Debería ver una sonrisa en la matriz de LED de micro: bits.
  5. Presione los botones del micro: bit y verifique que vea algo como lo siguiente:

Responder a las pulsaciones de botones

Para responder a las pulsaciones del botón micro: bit, actualice readLoop para comprobar si los datos que ha recibido son un object con una propiedad de button . Luego, buttonPushed de buttonPushed para manejar 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 micro: bit, la pantalla en la matriz de LED debería cambiar. Utilice 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();
  }
}

Intentalo

Ahora, cuando presiona uno de los botones micro: bit, la matriz de LED debería cambiar a una cara feliz o una cara triste.

  1. Vuelve a cargar la página.
  2. Haga clic en el botón Conectar .
  3. En el cuadro de diálogo del selector de puerto serie, seleccione el dispositivo BBC micro: bit y haga clic en Conectar .
  4. Debería ver una sonrisa en la matriz de LED de micro: bits.
  5. Presione los botones del micro: bit y verifique que la matriz de LED cambie.

El último paso es conectar la función de desconexión para cerrar el puerto cuando el usuario haya terminado.

Cierre 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, llame a disconnect() y actualice la interfaz de usuario para indicar que la página ya no está conectada al dispositivo serie.

script.js - clickConnect()

// CODELAB: Add disconnect code here.
if (port) {
  await disconnect();
  toggleUIConnected(false);
  return;
}

Cierra los arroyos y el puerto

En la función de disconnect , necesitamos cerrar el flujo de entrada, cerrar el flujo de salida y cerrar el puerto. Para cerrar el flujo de entrada, llame a reader.cancel() . La llamada para cancel es asincrónica, 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, obtenga un writer , llame a close() y espere a que se outputDone objeto outputDone :

script.js - disconnect()

// CODELAB: Close the output stream.
if (outputStream) {
  await outputStream.getWriter().close();
  await outputDone;
  outputStream = null;
  outputDone = null;
}

Finalmente, cierre el puerto serie y espere a que se cierre:

script.js - disconnect()

// CODELAB: Close the port.
await port.close();
port = null;

Intentalo

Ahora, puede abrir y cerrar el puerto serie a voluntad.

  1. Vuelve a cargar la página.
  2. Haga clic en el botón Conectar .
  3. En el cuadro de diálogo del selector de puerto serie, seleccione el dispositivo BBC micro: bit y haga clic en Conectar.
  4. Debería ver una sonrisa en la matriz de LED de micro: bit
  5. Presione el botón Desconectar y verifique que la matriz de LED se apague y no haya errores en la consola.

¡Felicidades! Ha creado con éxito su primera aplicación web que utiliza la API de serie web.

Esté atento a https://goo.gle/fugu-api-tracker para obtener lo último en la API de serie web y todas las otras nuevas e interesantes capacidades web en las que está trabajando el equipo de Chrome.