1. Introduzione
Ultimo aggiornamento: 21/07/2020
Cosa creerai
In questo codelab, creerai una pagina web che utilizza l'API Web Serial per interagire con una scheda BBC micro:bit per mostrare immagini sulla sua matrice LED 5x5. Scoprirai l'API Web Serial e come utilizzare i flussi leggibili, scrivibili e di trasformazione per comunicare con i dispositivi seriali tramite il browser.

Cosa imparerai a fare
- Come aprire e chiudere una porta seriale web
- Come utilizzare un ciclo di lettura per gestire i dati di un flusso di input
- Come inviare i dati tramite uno stream di scrittura
Che cosa ti serve
- Una scheda BBC micro:bit con l'ultimo firmware Espruino
- Una versione recente di Chrome (80 o successive)
- Conoscenza di HTML, CSS, JavaScript e Chrome DevTools
Abbiamo scelto di utilizzare micro:bit per questo codelab perché è conveniente, offre alcuni input (pulsanti) e output (display LED 5x5) e può fornire input e output aggiuntivi. Per informazioni dettagliate sulle funzionalità di micro:bit, consulta la pagina di BBC micro:bit sul sito di Espruino.
2. Informazioni sull'API Web Serial
L'API Web Serial consente ai siti web di leggere e scrivere su un dispositivo seriale con script. L'API collega il web e il mondo fisico consentendo ai siti web di comunicare con dispositivi seriali, come microcontrollori e stampanti 3D.
Esistono molti esempi di software di controllo creati utilizzando la tecnologia web. Ad esempio:
In alcuni casi, questi siti web comunicano con il dispositivo tramite un'applicazione agente nativa installata manualmente dall'utente. In altri casi, l'applicazione viene fornita in un'applicazione nativa pacchettizzata tramite un framework come Electron. In altri casi, l'utente deve eseguire un passaggio aggiuntivo, ad esempio copiare un'applicazione compilata sul dispositivo con un'unità flash USB.
L'esperienza utente può essere migliorata fornendo una comunicazione diretta tra il sito e il dispositivo che controlla.
3. Preparazione
Ottieni il codice
Abbiamo inserito tutto il necessario per questo codelab in un progetto Glitch.
- Apri una nuova scheda del browser e vai alla pagina https://web-serial-codelab-start.glitch.me/.
- Fai clic sul link Remixa Glitch per creare la tua versione del progetto iniziale.
- Fai clic sul pulsante Mostra e poi scegli In una nuova finestra per vedere il codice in azione.
4. Aprire una connessione seriale
Controllare se l'API Web Serial è supportata
La prima cosa da fare è verificare se l'API Web Serial è supportata nel browser attuale. Per farlo, controlla se serial è presente in navigator.
Nell'evento DOMContentLoaded, aggiungi il seguente codice al tuo progetto:
script.js - DOMContentLoaded
// CODELAB: Add feature detection here.
const notSupported = document.getElementById('notSupported');
notSupported.classList.toggle('hidden', 'serial' in navigator);
Controlla se Web Serial è supportato. In caso affermativo, questo codice nasconde il banner che indica che Web Serial non è supportato.
Prova
- Carica la pagina.
- Verifica che nella pagina non venga visualizzato un banner rosso che indica che Web Serial non è supportato.
Apri la porta seriale
Successivamente, dobbiamo aprire la porta seriale. Come la maggior parte delle altre API moderne, l'API Web Serial è asincrona. In questo modo, l'interfaccia utente non viene bloccata durante l'attesa dell'input, ma è importante anche perché i dati seriali possono essere ricevuti dalla pagina web in qualsiasi momento e abbiamo bisogno di un modo per ascoltarli.
Poiché un computer può avere più dispositivi seriali, quando il browser tenta di richiedere una porta, chiede all'utente di scegliere a quale dispositivo connettersi.
Aggiungi il seguente codice al tuo progetto:
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 chiamata requestPort chiede all'utente a quale dispositivo vuole connettersi. La chiamata a port.open apre la porta. Dobbiamo anche fornire la velocità con cui vogliamo comunicare con il dispositivo seriale. BBC micro:bit utilizza una connessione a 9600 baud tra il chip da USB a seriale e il processore principale.
Colleghiamo anche il pulsante Connetti e facciamo in modo che chiami connect() quando l'utente fa clic.
Aggiungi il seguente codice al tuo progetto:
script.js - clickConnect()
// CODELAB: Add connect code here.
await connect();
Prova
Il nostro progetto ora ha il minimo indispensabile per iniziare. Se fai clic sul pulsante Connetti, all'utente viene chiesto di selezionare il dispositivo seriale a cui connettersi, dopodiché viene stabilita la connessione a micro:bit.
- Ricarica la pagina.
- Fai clic sul pulsante Connetti.
- Nella finestra di dialogo di selezione della porta seriale, seleziona il dispositivo BBC micro:bit e fai clic su Connetti.
- Nella scheda dovresti vedere un'icona che indica che ti sei connesso a un dispositivo seriale:

Configurare un flusso di input per ascoltare i dati dalla porta seriale
Una volta stabilita la connessione, dobbiamo configurare un flusso di input e un lettore per leggere i dati dal dispositivo. Per prima cosa, otteniamo lo stream leggibile dalla porta chiamando port.readable. Poiché sappiamo che riceveremo un testo dal dispositivo, lo invieremo tramite un decodificatore di testo. Successivamente, otterremo un lettore e avvieremo il ciclo di lettura.
Aggiungi il seguente codice al tuo progetto:
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();
Il ciclo di lettura è una funzione asincrona che viene eseguita in un ciclo e attende i contenuti senza bloccare il thread principale. Quando arrivano nuovi dati, il lettore restituisce due proprietà: value e un valore booleano done. Se done è vero, la porta è stata chiusa o non vengono più ricevuti dati.
Aggiungi il seguente codice al tuo progetto:
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;
}
}
Prova
Il nostro progetto ora può connettersi al dispositivo e aggiungere i dati ricevuti dal dispositivo all'elemento log.
- Ricarica la pagina.
- Fai clic sul pulsante Connetti.
- Nella finestra di dialogo di selezione della porta seriale, seleziona il dispositivo BBC micro:bit e fai clic su Connetti.
- Dovresti vedere il logo di Espruino:

Configurare un flusso di output per inviare dati alla porta seriale
La comunicazione seriale è in genere bidirezionale. Oltre a ricevere dati dalla porta seriale, vogliamo anche inviare dati alla porta. Come per il flusso di input, invieremo solo testo tramite il flusso di output a micro:bit.
Per prima cosa, crea uno stream del codificatore di testo e reindirizzalo 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;
Quando è collegata tramite seriale con il firmware Espruino, la scheda BBC micro:bit funge da ciclo di lettura-valutazione-stampa (REPL) JavaScript, simile a quello che si ottiene in una shell Node.js. A questo punto, dobbiamo fornire un metodo per inviare i dati allo stream. Il codice riportato di seguito recupera un writer dal flusso di output e poi utilizza write per inviare ogni riga. Ogni riga inviata include un carattere di nuova riga (\n), per indicare a micro:bit di valutare il comando inviato.
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();
Per portare il sistema in uno stato noto e impedire che ripeta i caratteri che gli inviamo, dobbiamo inviare un CTRL+C e disattivare l'echo.
script.js - connect()
// CODELAB: Send CTRL-C and turn off echo on REPL
writeToStream('\x03', 'echo(false);');
Prova
Il nostro progetto ora può inviare e ricevere dati da micro:bit. Verifichiamo di poter inviare correttamente un comando:
- Ricarica la pagina.
- Fai clic sul pulsante Connetti.
- Nella finestra di dialogo di selezione della porta seriale, seleziona il dispositivo BBC micro:bit e fai clic su Connetti.
- Apri la scheda Console in Chrome DevTools e digita
writeToStream('console.log("yes")');
Dovresti vedere qualcosa di simile stampato sulla pagina:

5. Controllare la matrice LED
Crea la stringa della griglia a matrice
Per controllare la matrice LED su micro:bit, dobbiamo chiamare show(). Questo metodo mostra la grafica sullo schermo LED 5x5 integrato. Accetta un numero binario o una stringa.
Itereremo le caselle di controllo e genereremo un array di 1 e 0 che indicano quali sono selezionate e quali no. Dobbiamo quindi invertire l'array, perché l'ordine delle caselle di controllo è l'opposto dell'ordine dei LED nella matrice. Successivamente, convertiamo l'array in una stringa e creiamo il comando da inviare 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('')})`);
Collega le caselle di controllo per aggiornare la matrice
Successivamente, dobbiamo ascoltare le modifiche alle caselle di controllo e, se cambiano, inviare le informazioni a micro:bit. Nel codice di rilevamento delle funzionalità (// CODELAB: Add feature detection here.), aggiungi la seguente riga:
script.js - DOMContentLoaded
initCheckboxes();
Reimpostiamo anche la griglia quando micro:bit viene connesso per la prima volta, in modo che mostri una faccina sorridente. La funzione drawGrid() è già fornita. Questa funzione funziona in modo simile a sendGrid(): accetta un array di 1 e 0 e seleziona le caselle di controllo in modo appropriato.
script.js - clickConnect()
// CODELAB: Reset the grid on connect here.
drawGrid(GRID_HAPPY);
sendGrid();
Prova
Ora, quando la pagina apre una connessione a micro:bit, invia una faccina sorridente. Se selezioni le caselle di controllo, la visualizzazione sulla matrice LED verrà aggiornata.
- Ricarica la pagina.
- Fai clic sul pulsante Connetti.
- Nella finestra di dialogo di selezione della porta seriale, seleziona il dispositivo BBC micro:bit e fai clic su Connetti.
- Dovresti vedere un sorriso sulla matrice LED di micro:bit.
- Disegna un pattern diverso sulla matrice LED modificando le caselle di controllo.
6. Collega i pulsanti di micro:bit
Aggiungere un evento di orologio sui pulsanti di micro:bit
Su micro:bit sono presenti due pulsanti, uno su ciascun lato della matrice LED. Espruino fornisce una funzione setWatch che invia un evento/callback quando viene premuto il pulsante. Poiché vogliamo ascoltare entrambi i pulsanti, renderemo la nostra funzione generica e faremo in modo che stampi i dettagli dell'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);
Successivamente, dobbiamo collegare entrambi i pulsanti (denominati BTN1 e BTN2 sulla scheda micro:bit) ogni volta che la porta seriale è connessa al dispositivo.
script.js - clickConnect()
// CODELAB: Initialize micro:bit buttons.
watchButton('BTN1');
watchButton('BTN2');
Prova
Oltre a mostrare una faccina felice quando è connesso, se premi uno dei pulsanti su micro:bit, viene aggiunto del testo alla pagina che indica quale pulsante è stato premuto. Molto probabilmente, ogni personaggio si troverà su una riga separata.
- Ricarica la pagina.
- Fai clic sul pulsante Connetti.
- Nella finestra di dialogo di selezione della porta seriale, seleziona il dispositivo BBC micro:bit e fai clic su Connetti.
- Dovresti vedere un sorriso sulla matrice LED di micro:bit.
- Premi i pulsanti su micro:bit e verifica che venga aggiunto nuovo testo alla pagina con i dettagli del pulsante premuto.
7. Utilizzare un flusso di trasformazione per analizzare i dati in entrata
Gestione di base dello stream
Quando viene premuto uno dei pulsanti di micro:bit, micro:bit invia i dati alla porta seriale tramite uno stream. Gli stream sono molto utili, ma possono anche essere una sfida perché non riceverai necessariamente tutti i dati contemporaneamente e potrebbero essere suddivisi in blocchi arbitrari.
Al momento l'app stampa il flusso in entrata man mano che arriva (in readLoop). Nella maggior parte dei casi, ogni carattere si trova su una riga separata, ma non è molto utile. Idealmente, lo stream deve essere analizzato in singole righe e ogni messaggio deve essere visualizzato su una riga separata.
Trasformare i flussi con TransformStream
Per farlo, possiamo utilizzare uno stream di trasformazione ( TransformStream), che consente di analizzare lo stream in entrata e restituire i dati analizzati. Un flusso di trasformazione può trovarsi tra l'origine del flusso (in questo caso, micro:bit) e qualsiasi elemento che lo utilizza (in questo caso readLoop) e può applicare una trasformazione arbitraria prima che venga utilizzato. Pensa a una catena di montaggio: man mano che un widget scende lungo la linea, ogni passaggio della linea lo modifica, in modo che quando arriva alla destinazione finale sia un widget perfettamente funzionante.
Per saperne di più, consulta la pagina Concetti dell'API Streams di MDN.
Trasforma lo stream con LineBreakTransformer
Creiamo una classe LineBreakTransformer, che accetterà uno stream e lo suddividerà in blocchi in base agli interruzioni di riga (\r\n). La classe ha bisogno di due metodi, transform e flush. Il metodo transform viene chiamato ogni volta che lo stream riceve nuovi dati. Può accodare i dati o salvarli per un utilizzo futuro. Il metodo flush viene chiamato quando lo stream viene chiuso e gestisce i dati non ancora elaborati.
Nel nostro metodo transform, aggiungeremo nuovi dati a container e poi verificheremo se sono presenti interruzioni di riga in container. Se presenti, dividi la stringa in un array, poi scorri le righe e chiama controller.enqueue() per inviare le righe analizzate.
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 lo stream viene chiuso, eliminiamo semplicemente i dati rimanenti nel contenitore utilizzando enqueue.
script.js - LineBreakTransformer.flush()
// CODELAB: Flush the stream.
controller.enqueue(this.container);
Infine, dobbiamo convogliare lo stream in entrata attraverso il nuovo LineBreakTransformer. Il nostro flusso di input originale è stato inviato solo tramite un TextDecoderStream, quindi dobbiamo aggiungere un pipeThrough aggiuntivo per inviarlo tramite il nostro nuovo 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()));
Prova
Ora, quando premi uno dei pulsanti di micro:bit, i dati stampati devono essere restituiti su una singola riga.
- Ricarica la pagina.
- Fai clic sul pulsante Connetti.
- Nella finestra di dialogo di selezione della porta seriale, seleziona il dispositivo BBC micro:bit e fai clic su Connetti.
- Dovresti vedere un sorriso sulla matrice LED di micro:bit.
- Premi i pulsanti su micro:bit e verifica che venga visualizzato un messaggio simile al seguente:

Trasforma lo stream con JSONTransformer
Potremmo provare ad analizzare la stringa in JSON in readLoop, ma creiamo invece un trasformatore JSON molto semplice che trasformerà i dati in un oggetto JSON. Se i dati non sono JSON validi, restituisci semplicemente ciò che è stato ricevuto.
script.js - JSONTransformer.transform
// CODELAB: Attempt to parse JSON content
try {
controller.enqueue(JSON.parse(chunk));
} catch (e) {
controller.enqueue(chunk);
}
Successivamente, convoglia il flusso attraverso JSONTransformer, dopo che è passato attraverso LineBreakTransformer. In questo modo, possiamo mantenere il nostro JSONTransformer semplice, poiché sappiamo che il JSON verrà inviato sempre su una sola riga.
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()));
Prova
Ora, quando premi uno dei pulsanti di micro:bit, dovresti vedere [object Object] stampato sulla pagina.
- Ricarica la pagina.
- Fai clic sul pulsante Connetti.
- Nella finestra di dialogo di selezione della porta seriale, seleziona il dispositivo BBC micro:bit e fai clic su Connetti.
- Dovresti vedere un sorriso sulla matrice LED di micro:bit.
- Premi i pulsanti su micro:bit e verifica che venga visualizzato un risultato simile al seguente:
Rispondere alla pressione dei pulsanti
Per rispondere alle pressioni dei pulsanti di micro:bit, aggiorna readLoop per verificare se i dati ricevuti sono un object con una proprietà button. Quindi, chiama buttonPushed per gestire la pressione del pulsante.
script.js - readLoop()
const { value, done } = await reader.read();
if (value && value.button) {
buttonPushed(value);
} else {
log.textContent += value + '\n';
}
Quando viene premuto un pulsante di micro:bit, il display sulla matrice LED dovrebbe cambiare. Utilizza il seguente codice per impostare la matrice:
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();
}
}
Prova
Ora, quando premi uno dei pulsanti di micro:bit, la matrice LED dovrebbe cambiare in una faccina felice o triste.
- Ricarica la pagina.
- Fai clic sul pulsante Connetti.
- Nella finestra di dialogo di selezione della porta seriale, seleziona il dispositivo BBC micro:bit e fai clic su Connetti.
- Dovresti vedere un sorriso sulla matrice LED di micro:bit.
- Premi i pulsanti su micro:bit e verifica che la matrice LED cambi.
8. Chiusura della porta seriale
Il passaggio finale consiste nel collegare la funzionalità di disconnessione per chiudere la porta quando l'utente ha terminato.
Chiudi la porta quando l'utente fa clic sul pulsante Connetti/Disconnetti
Quando l'utente fa clic sul pulsante Connetti/Disconnetti, dobbiamo chiudere la connessione. Se la porta è già aperta, chiama disconnect() e aggiorna la UI per indicare che la pagina non è più connessa al dispositivo seriale.
script.js - clickConnect()
// CODELAB: Add disconnect code here.
if (port) {
await disconnect();
toggleUIConnected(false);
return;
}
Chiudi gli stream e la porta
Nella funzione disconnect, dobbiamo chiudere lo stream di input, lo stream di output e la porta. Per chiudere lo stream di input, chiama reader.cancel(). La chiamata a cancel è asincrona, quindi dobbiamo utilizzare await per attendere il completamento:
script.js - disconnect()
// CODELAB: Close the input stream (reader).
if (reader) {
await reader.cancel();
await inputDone.catch(() => {});
reader = null;
inputDone = null;
}
Per chiudere lo stream di output, ottieni un writer, chiama close() e attendi che l'oggetto outputDone venga chiuso:
script.js - disconnect()
// CODELAB: Close the output stream.
if (outputStream) {
await outputStream.getWriter().close();
await outputDone;
outputStream = null;
outputDone = null;
}
Infine, chiudi la porta seriale e attendi che venga chiusa:
script.js - disconnect()
// CODELAB: Close the port.
await port.close();
port = null;
Prova
Ora puoi aprire e chiudere la porta seriale a tuo piacimento.
- Ricarica la pagina.
- Fai clic sul pulsante Connetti.
- Nella finestra di dialogo di selezione della porta seriale, seleziona il dispositivo BBC micro:bit e fai clic su Connetti.
- Dovresti vedere un sorriso sulla matrice LED di micro:bit
- Premi il pulsante Disconnetti e verifica che la matrice LED si spenga e che non ci siano errori nella console.
9. Complimenti
Complimenti! Hai creato la tua prima app web che utilizza l'API Web Serial.
Tieni d'occhio https://goo.gle/fugu-api-tracker per scoprire le ultime novità sull'API Web Serial e su tutte le altre nuove entusiasmanti funzionalità web su cui sta lavorando il team di Chrome.