1. Introdução
Última atualização:21/07/2020
O que você vai criar
Neste codelab, você vai criar uma página da Web que usa a API Web Serial para interagir com uma placa BBC micro:bit e mostrar imagens na matriz de LED 5x5. Você vai aprender sobre a API Web Serial e como usar fluxos legíveis, graváveis e de transformação para se comunicar com dispositivos seriais pelo navegador.

O que você vai aprender
- Como abrir e fechar uma porta serial da Web
- Como usar um loop de leitura para processar dados de um fluxo de entrada
- Como enviar dados por um stream de gravação
O que é necessário
- Uma placa BBC micro:bit com o firmware Espruino mais recente
- Uma versão recente do Chrome (80 ou mais recente)
- Conhecimento de HTML, CSS, JavaScript e Chrome DevTools
Escolhemos usar o micro:bit neste codelab porque ele é acessível, oferece algumas entradas (botões) e saídas (display de LED 5x5) e pode fornecer entradas e saídas adicionais. Consulte a página do BBC micro:bit no site do Espruino para saber o que o micro:bit pode fazer.
2. Sobre a API Web Serial
A API Web Serial permite que os sites leiam e gravem conteúdo de um dispositivo serial com scripts. A API conecta a Web e o mundo físico, permitindo que sites se comuniquem com dispositivos seriais, como microcontroladores e impressoras 3D.
Há muitos exemplos de software de controle criados com tecnologia da Web. Exemplo:
- Arduino Create
- Configurador do Betaflight
- Espruino IDE (em inglês)
- MakeCode
Em alguns casos, esses sites se comunicam com o dispositivo por um aplicativo de agente nativo instalado manualmente pelo usuário. Em outros casos, o aplicativo é entregue em um app nativo empacotado por um framework como o Electron. Em outros casos, o usuário precisa realizar uma etapa extra, como copiar um aplicativo compilado para o dispositivo com um pen drive USB.
A experiência do usuário pode ser melhorada com a comunicação direta entre o site e o dispositivo que ele está controlando.
3. Etapas da configuração
Buscar o código
Colocamos tudo o que você precisa para este codelab em um projeto do Glitch.
- Abra uma nova guia do navegador e acesse https://web-serial-codelab-start.glitch.me/.
- Clique no link Remix Glitch para criar sua própria versão do projeto inicial.
- Clique no botão Mostrar e escolha Em uma nova janela para ver seu código em ação.
4. Abrir uma conexão serial
Verificar se a API Web Serial é compatível
Primeiro, verifique se a API Web Serial é compatível com o navegador atual. Para isso, verifique se serial está em navigator.
No evento DOMContentLoaded, adicione o seguinte código ao projeto:
script.js - DOMContentLoaded
// CODELAB: Add feature detection here.
const notSupported = document.getElementById('notSupported');
notSupported.classList.toggle('hidden', 'serial' in navigator);
Isso verifica se a API Web Serial é compatível. Se for, esse código vai ocultar o banner que informa que a API Web Serial não é compatível.
Testar
- Carregue a página.
- Verifique se a página não mostra um banner vermelho informando que a API Web Serial não é compatível.
Abra a porta serial.
Em seguida, precisamos abrir a porta serial. Como a maioria das outras APIs modernas, a API Web Serial é assíncrona. Isso evita que a interface seja bloqueada ao aguardar entrada, mas também é importante porque os dados seriais podem ser recebidos pela página da Web a qualquer momento, e precisamos de uma maneira de detectar isso.
Como um computador pode ter vários dispositivos seriais, quando o navegador tenta solicitar uma porta, ele pede que o usuário escolha com qual dispositivo se conectar.
Adicione o seguinte código ao projeto:
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 });
A chamada requestPort pergunta ao usuário a qual dispositivo ele quer se conectar. Chamar port.open abre a porta. Também precisamos informar a velocidade com que queremos nos comunicar com o dispositivo serial. O BBC micro:bit usa uma conexão de 9600 bauds entre o chip USB-serial e o processador principal.
Vamos também conectar o botão de conexão e fazer com que ele chame connect() quando o usuário clicar nele.
Adicione o seguinte código ao projeto:
script.js - clickConnect()
// CODELAB: Add connect code here.
await connect();
Testar
Nosso projeto agora tem o mínimo necessário para começar. Ao clicar no botão Conectar, o usuário precisa selecionar o dispositivo serial a ser conectado e, em seguida, o micro:bit.
- Atualize a página.
- Clique no botão Conectar.
- Na caixa de diálogo do seletor de porta serial, selecione o dispositivo BBC micro:bit e clique em Conectar.
- Na guia, você vai encontrar um ícone indicando que se conectou a um dispositivo serial:

Configurar um fluxo de entrada para detectar dados da porta serial
Depois que a conexão for estabelecida, precisamos configurar um fluxo de entrada e um leitor para ler os dados do dispositivo. Primeiro, vamos receber o fluxo legível da porta chamando port.readable. Como sabemos que vamos receber texto do dispositivo, vamos transmitir por um decodificador de texto. Em seguida, vamos receber um leitor e iniciar o loop de leitura.
Adicione o seguinte código ao projeto:
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();
O loop de leitura é uma função assíncrona que é executada em um loop e aguarda conteúdo sem bloquear a linha de execução principal. Quando novos dados chegam, o leitor retorna duas propriedades: o value e um booleano done. Se done for verdadeiro, a porta foi fechada ou não há mais dados chegando.
Adicione o seguinte código ao projeto:
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;
}
}
Testar
Agora, nosso projeto pode se conectar ao dispositivo e anexar todos os dados recebidos dele ao elemento de registro.
- Atualize a página.
- Clique no botão Conectar.
- Na caixa de diálogo do seletor de porta serial, selecione o dispositivo BBC micro:bit e clique em Conectar.
- O logotipo do Espruino vai aparecer:

Configurar um fluxo de saída para enviar dados à porta serial
A comunicação serial geralmente é bidirecional. Além de receber dados da porta serial, também queremos enviar dados para ela. Assim como no fluxo de entrada, só vamos enviar texto pelo fluxo de saída para o micro:bit.
Primeiro, crie um stream de codificador de texto e transmita o stream para 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;
Quando conectado por serial com o firmware Espruino, o micro:bit da BBC funciona como um loop de leitura-avaliação-impressão (REPL) em JavaScript, semelhante ao que você encontra em um shell do Node.js. Em seguida, precisamos fornecer um método para enviar dados ao fluxo. O código abaixo recebe um gravador do fluxo de saída e usa write para enviar cada linha. Cada linha enviada inclui um caractere de nova linha (\n) para informar ao micro:bit que ele precisa avaliar o 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 o sistema em um estado conhecido e impedir que ele retorne os caracteres enviados, precisamos enviar um CTRL-C e desativar o eco.
script.js - connect()
// CODELAB: Send CTRL-C and turn off echo on REPL
writeToStream('\x03', 'echo(false);');
Testar
Agora nosso projeto pode enviar e receber dados do micro:bit. Vamos verificar se podemos enviar um comando corretamente:
- Atualize a página.
- Clique no botão Conectar.
- Na caixa de diálogo do seletor de porta serial, selecione o dispositivo BBC micro:bit e clique em Conectar.
- Abra a guia Console no Chrome DevTools e digite
writeToStream('console.log("yes")');
Você vai ver algo assim impresso na página:

5. Controlar a matriz de LED
Criar a string da grade de matriz
Para controlar a matriz de LED no micro:bit, precisamos chamar show(). Esse método mostra gráficos na tela de LED 5x5 integrada. Isso usa um número binário ou uma string.
Vamos iterar pelas caixas de seleção e gerar uma matriz de 1s e 0s indicando quais estão marcadas e quais não estão. Em seguida, precisamos inverter a matriz, porque a ordem das caixas de seleção é o oposto da ordem dos LEDs na matriz. Em seguida, convertemos a matriz em uma string e criamos o comando para enviar ao 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('')})`);
Conecte as caixas de seleção para atualizar a matriz
Em seguida, precisamos detectar mudanças nas caixas de seleção e, se elas mudarem, enviar essas informações para o micro:bit. No código de detecção de recursos (// CODELAB: Add feature detection here.), adicione a seguinte linha:
script.js - DOMContentLoaded
initCheckboxes();
Vamos também redefinir a grade quando o micro:bit for conectado pela primeira vez para que ele mostre um rosto feliz. A função drawGrid() já foi fornecida. Essa função funciona de maneira semelhante a sendGrid(). Ela recebe uma matriz de 1s e 0s e marca as caixas de seleção conforme apropriado.
script.js - clickConnect()
// CODELAB: Reset the grid on connect here.
drawGrid(GRID_HAPPY);
sendGrid();
Testar
Agora, quando a página abrir uma conexão com o micro:bit, ela vai enviar um rosto feliz. Clicar nas caixas de seleção atualiza a exibição na matriz de LED.
- Atualize a página.
- Clique no botão Conectar.
- Na caixa de diálogo do seletor de porta serial, selecione o dispositivo BBC micro:bit e clique em Conectar.
- Um sorriso vai aparecer na matriz de LED do micro:bit.
- Desenhe um padrão diferente na matriz de LED mudando as caixas de seleção.
6. Conecte os botões do micro:bit
Adicionar um evento de observação aos botões do micro:bit
Há dois botões no micro:bit, um de cada lado da matriz de LED. O Espruino fornece uma função setWatch que envia um evento/callback quando o botão é pressionado. Como queremos ouvir os dois botões, vamos tornar nossa função genérica e fazer com que ela imprima os detalhes do 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);
Em seguida, precisamos conectar os dois botões (chamados BTN1 e BTN2 na placa micro:bit) sempre que a porta serial for conectada ao dispositivo.
script.js - clickConnect()
// CODELAB: Initialize micro:bit buttons.
watchButton('BTN1');
watchButton('BTN2');
Testar
Além de mostrar um rosto feliz quando conectado, pressionar qualquer um dos botões no micro:bit adiciona texto à página indicando qual botão foi pressionado. Provavelmente, cada caractere vai estar em uma linha.
- Atualize a página.
- Clique no botão Conectar.
- Na caixa de diálogo do seletor de porta serial, selecione o dispositivo BBC micro:bit e clique em Conectar.
- Um sorriso vai aparecer na matriz de LED do micro:bit.
- Pressione os botões no micro:bit e verifique se ele adiciona um novo texto à página com detalhes do botão pressionado.
7. Usar um fluxo de transformação para analisar dados recebidos
Processamento básico de streams
Quando um dos botões do micro:bit é pressionado, ele envia dados para a porta serial por um fluxo. Os fluxos são muito úteis, mas também podem ser um desafio porque nem sempre você recebe todos os dados de uma vez, e eles podem ser divididos arbitrariamente.
No momento, o app imprime o fluxo de entrada à medida que ele chega (em readLoop). Na maioria dos casos, cada caractere fica em uma linha separada, mas isso não é muito útil. O ideal é que o stream seja analisado em linhas individuais, e cada mensagem seja mostrada em uma linha separada.
Transformar streams com TransformStream
Para isso, podemos usar um stream de transformação ( TransformStream), que permite analisar o stream de entrada e retornar dados analisados. Um fluxo de transformação pode ficar entre a origem do fluxo (neste caso, o micro:bit) e o que está consumindo o fluxo (neste caso, readLoop), e pode aplicar uma transformação arbitrária antes de ser consumido. Pense nisso como uma linha de montagem: à medida que um widget desce a linha, cada etapa modifica o widget para que, quando ele chegar ao destino final, seja um widget totalmente funcional.
Para mais informações, consulte Conceitos da API Streams da MDN.
Transformar o stream com LineBreakTransformer
Vamos criar uma classe LineBreakTransformer, que vai receber um fluxo e dividi-lo com base em quebras de linha (\r\n). A classe precisa de dois métodos, transform e flush. O método transform é chamado sempre que novos dados são recebidos pelo fluxo. Ele pode enfileirar os dados ou salvá-los para mais tarde. O método flush é chamado quando o fluxo é fechado e processa todos os dados que ainda não foram processados.
No método transform, vamos adicionar novos dados a container e verificar se há quebras de linha em container. Se houver, divida em uma matriz e itere pelas linhas, chamando controller.enqueue() para enviar as linhas analisadas.
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));
Quando o stream for fechado, vamos simplesmente transferir os dados restantes no contêiner usando enqueue.
script.js - LineBreakTransformer.flush()
// CODELAB: Flush the stream.
controller.enqueue(this.container);
Por fim, precisamos transmitir o fluxo de entrada pelo novo LineBreakTransformer. O fluxo de entrada original foi transmitido apenas por um TextDecoderStream. Portanto, precisamos adicionar um pipeThrough extra para transmitir pelo novo 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()));
Testar
Agora, quando você pressionar um dos botões do micro:bit, os dados impressos serão retornados em uma única linha.
- Atualize a página.
- Clique no botão Conectar.
- Na caixa de diálogo do seletor de porta serial, selecione o dispositivo BBC micro:bit e clique em Conectar.
- Um sorriso vai aparecer na matriz de LED do micro:bit.
- Pressione os botões no micro:bit e verifique se algo parecido com o seguinte aparece:

Transformar o stream com JSONTransformer
Podemos tentar analisar a string em JSON no readLoop, mas, em vez disso, vamos criar um transformador JSON muito simples que vai transformar os dados em um objeto JSON. Se os dados não forem um JSON válido, retorne o que foi recebido.
script.js - JSONTransformer.transform
// CODELAB: Attempt to parse JSON content
try {
controller.enqueue(JSON.parse(chunk));
} catch (e) {
controller.enqueue(chunk);
}
Em seguida, transmita o fluxo pelo JSONTransformer depois que ele passar pelo LineBreakTransformer. Isso nos permite manter nosso JSONTransformer simples, já que sabemos que o JSON só será enviado em uma única linha.
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()));
Testar
Agora, quando você pressionar um dos botões do micro:bit, verá [object Object] impresso na página.
- Atualize a página.
- Clique no botão Conectar.
- Na caixa de diálogo do seletor de porta serial, selecione o dispositivo BBC micro:bit e clique em Conectar.
- Um sorriso vai aparecer na matriz de LED do micro:bit.
- Pressione os botões no micro:bit e verifique se você vê algo como o seguinte:
Responder a pressionamentos de botões
Para responder aos toques no botão do micro:bit, atualize readLoop para verificar se os dados recebidos são um object com uma propriedade button. Em seguida, chame buttonPushed para processar o toque no botão.
script.js - readLoop()
const { value, done } = await reader.read();
if (value && value.button) {
buttonPushed(value);
} else {
log.textContent += value + '\n';
}
Quando um botão do micro:bit é pressionado, o display na matriz de LED muda. Use o código a seguir para definir a 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();
}
}
Testar
Agora, quando você pressionar um dos botões do micro:bit, a matriz de LED vai mudar para um rosto feliz ou triste.
- Atualize a página.
- Clique no botão Conectar.
- Na caixa de diálogo do seletor de porta serial, selecione o dispositivo BBC micro:bit e clique em Conectar.
- Um sorriso vai aparecer na matriz de LED do micro:bit.
- Pressione os botões no micro:bit e verifique se a matriz de LED muda.
8. Como fechar a porta serial
A etapa final é conectar a funcionalidade de desconexão para fechar a porta quando o usuário terminar.
Fechar a porta quando o usuário clicar no botão "Conectar/Desconectar"
Quando o usuário clica no botão Conectar/Desconectar, precisamos fechar a conexão. Se a porta já estiver aberta, chame disconnect() e atualize a interface para indicar que a página não está mais conectada ao dispositivo serial.
script.js - clickConnect()
// CODELAB: Add disconnect code here.
if (port) {
await disconnect();
toggleUIConnected(false);
return;
}
Fechar os streams e a porta
Na função disconnect, precisamos fechar o stream de entrada, o stream de saída e a porta. Para fechar o fluxo de entrada, chame reader.cancel(). A chamada para cancel é assíncrona, então precisamos usar await para aguardar a conclusão:
script.js - disconnect()
// CODELAB: Close the input stream (reader).
if (reader) {
await reader.cancel();
await inputDone.catch(() => {});
reader = null;
inputDone = null;
}
Para fechar o fluxo de saída, receba um writer, chame close() e aguarde o fechamento do objeto outputDone:
script.js - disconnect()
// CODELAB: Close the output stream.
if (outputStream) {
await outputStream.getWriter().close();
await outputDone;
outputStream = null;
outputDone = null;
}
Por fim, feche a porta serial e aguarde:
script.js - disconnect()
// CODELAB: Close the port.
await port.close();
port = null;
Testar
Agora você pode abrir e fechar a porta serial quando quiser.
- Atualize a página.
- Clique no botão Conectar.
- Na caixa de diálogo do seletor de porta serial, selecione o dispositivo BBC micro:bit e clique em Conectar.
- Um sorriso vai aparecer na matriz de LED do micro:bit.
- Pressione o botão Desconectar e verifique se a matriz de LED se apaga e se não há erros no console.
9. Parabéns
Parabéns! Você criou seu primeiro app da Web usando a API Web Serial.
Acompanhe https://goo.gle/fugu-api-tracker para saber as novidades sobre a API Web Serial e todos os outros recursos novos e interessantes da Web em que a equipe do Chrome está trabalhando.