Iniziare a utilizzare l'API Web Serial

Ultimo aggiornamento:08-11-2019

Cosa imparerai a realizzare

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 gli stream leggibili, scrivibili e trasformare per comunicare con i dispositivi seriali tramite il browser.

Cosa imparerai a fare:

  • Come aprire e chiudere una porta Web Serial
  • Come utilizzare un loop di lettura per gestire i dati da uno stream di input
  • Come inviare i dati tramite uno stream di scrittura

Che cosa ti serve

Abbiamo scelto di utilizzare il micro:bit per questo codelab perché è conveniente, offre alcuni input (pulsanti) e output (display LED 5x5) e può fornire input e output aggiuntivi. Consulta la pagina micro:bit BBC sul sito di Espruino per informazioni dettagliate sulle funzionalità del micro:bit.

L'API Web Serial consente ai siti web di leggere e scrivere su un dispositivo seriale con script. L'API collega il Web al mondo fisico, consentendo ai siti web di comunicare con dispositivi seriali, come microcontroller e stampanti 3D.

Esistono molti esempi di software di controllo creati utilizzando la tecnologia web. Ad esempio:

In alcuni casi, i siti web comunicano con il dispositivo tramite un'applicazione dell'agente nativo installata manualmente dall'utente. In altri casi, l'applicazione viene pubblicata in un'applicazione nativa in pacchetto tramite un framework come Electron. In altri casi, all'utente viene chiesto di eseguire un passaggio aggiuntivo, ad esempio copiare un'applicazione compilata sul dispositivo utilizzando un'unità flash USB.

L'esperienza utente può essere migliorata fornendo una comunicazione diretta tra il sito e il dispositivo che sta controllando.

Abilita l'API Web Serial

L'API Web Serial è attualmente in fase di sviluppo ed è disponibile solo protetta da un flag. Devi attivare il flag #enable-experimental-web-platform-features in chrome://flags.

Genera il codice

Abbiamo inserito tutto ciò che ti serve per questo codelab in un progetto Glitch.

  1. Apri una nuova scheda del browser e vai a https://web-serial-codelab-start.glitch.me/.
  2. Fai clic sul link Remix Glitch per creare la tua versione del progetto iniziale.
  3. Per visualizzare il codice in azione, fai clic sul pulsante Mostra e poi scegli In una nuova finestra.

Verificare 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 è in navigator.

Nell'evento DOMContentLoaded, aggiungi il seguente codice al tuo progetto:

script.js - DOMContentLoaded

// CODELAB: Add feature detection here.
if ('serial' in navigator) {
  const notSupported = document.getElementById('notSupported');
  notSupported.classList.add('hidden');
}

Questa opzione consente di verificare se il numero di serie web è supportato. In questo caso, questo codice nasconde il banner che indica che Web Serial non è supportato.

Prova

  1. Carica la pagina.
  2. Verifica che la pagina non mostri un banner rosso che indica che Web Serial non è supportato.

Apri la porta seriale

Quindi 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 si blocca quando è in attesa di input, ma è importante anche perché i dati seriali potrebbero essere ricevuti dalla pagina web in qualsiasi momento, pertanto abbiamo bisogno di un modo per ascoltarli.

Poiché un computer può avere più dispositivi seriali, quando il browser tenta di richiedere una porta, all'utente viene chiesto di scegliere con 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 a requestPort chiede all'utente il dispositivo a cui vuole connettersi. Chiamare il numero port.open apre la porta. Dobbiamo anche fornire la velocità alla quale vogliamo comunicare con il dispositivo seriale. Il micro:bit BBC utilizza una connessione 9600 baud tra il chip da USB a seriale e il processore principale.

Associa anche il pulsante Connetti e chiedi di chiamare connect() quando l'utente fa clic sull'icona.

Aggiungi il seguente codice al tuo progetto:

script.js - clickConnect()

// CODELAB: Add connect code here.
await connect();

Prova

Per iniziare, il nostro progetto non prevede alcun minimo. Se fai clic sul pulsante Connetti, all'utente verrà richiesto di selezionare il dispositivo seriale a cui connettersi e poi di collegarsi al micro:bit.

  1. Ricarica la pagina.
  2. Fai clic sul pulsante Connetti.
  3. Nella finestra di dialogo del selettore della porta seriale, seleziona il dispositivo micro:bit BBC e fai clic su Connetti.
  4. Nella scheda dovrebbe essere visualizzata un'icona che indica che hai effettuato la connessione a un dispositivo seriale:

Configura uno stream di input per ascoltare i dati dalla porta seriale

Una volta stabilita la connessione, dobbiamo configurare uno stream di input e un lettore per leggere i dati dal dispositivo. Innanzitutto, riceviamo lo stream leggibile dal trasferimento chiamando port.readable. Poiché sappiamo che il testo verrà restituito dal dispositivo, lo utilizzeremo per la digitazione di testo. Passiamo quindi a un lettore e avvia 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 loop di lettura è una funzione asincrona che viene eseguita in un loop e attende i contenuti senza bloccare il thread principale. Quando arrivano nuovi dati, il lettore restituisce due proprietà: value e done booleana. Se done è true, la porta è stata chiusa o i dati non sono disponibili.

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 può ora connettersi al dispositivo e aggiungere all'elemento di log tutti i dati ricevuti dal dispositivo.

  1. Ricarica la pagina.
  2. Fai clic sul pulsante Connetti.
  3. Nella finestra di dialogo del selettore della porta seriale, seleziona il dispositivo micro:bit BBC e fai clic su Connetti.
  4. Dovresti vedere il logo di Espruino:

Configura uno stream di output per inviare i 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 lo stream di input, invieremo il testo tramite micro stream al micro:bit.

Prima di tutto, crea uno stream di codificatore di testo e invia la relativa pipeline 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 micro:bit BBC funge da loop read-eval-print (REPL) di JavaScript, in modo simile a quello che trovi in una shell Node.js. Quindi, dobbiamo fornire un metodo per inviare dati allo stream. Il codice riportato di seguito recupera un writer dallo stream di output e utilizza write per inviare ogni riga. Ogni riga inviata include un carattere di nuova riga (\n) per indicare al 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 riportare il sistema in uno stato noto e impedirgli di ripetere l'eco dei caratteri che inviamo, dobbiamo inviare un comando CTRL-C e disattivare l'eco.

script.js - connect()

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

Prova

Il nostro progetto è ora in grado di inviare e ricevere dati dal micro:bit. Verifichiamo di poter inviare correttamente un comando:

  1. Ricarica la pagina.
  2. Fai clic sul pulsante Connetti.
  3. Nella finestra di dialogo del selettore della porta seriale, seleziona il dispositivo micro:bit BBC e fai clic su Connetti.
  4. Apri la scheda Console in Strumenti per sviluppatori di Chrome e digita
    writeToStream('console.log("yes")');

Nella pagina dovrebbe essere visualizzato un testo simile al seguente:

Crea la stringa della griglia della matrice

Per controllare la matrice LED sul micro:bit, dobbiamo chiamare show(). Questo metodo mostra le immagini sullo schermo LED 5x5 integrato. L'operazione richiede un numero binario o una stringa.

Eseguiremo l'iterazione delle caselle di controllo e genereremo un array di 1 e 0, che indicano quali sono selezionati e quali no. Dobbiamo quindi invertire l'array perché l'ordine delle nostre caselle di controllo è l'opposto dell'ordine dei LED nella matrice. Poi convertiamo l'array in una stringa e creiamo il comando da inviare 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('')})`);

Collega le caselle di controllo per aggiornare la matrice

In seguito dobbiamo ascoltare le modifiche nelle caselle di controllo e, se cambiano, inviare le informazioni al micro:bit. Nel codice di rilevamento delle funzionalità (// CODELAB: Add feature detection here.), aggiungi la seguente riga:

script.js - DOMContentLoaded

initCheckboxes();

Reimposta anche la griglia quando il micro:bit viene collegato per la prima volta, in modo che mostri un volto felice. La funzione drawGrid() è già stata fornita. Questa funzione funziona in modo simile a sendGrid(); richiede un array di 1s e 0s e seleziona le caselle di controllo appropriate.

script.js - clickConnect()

// CODELAB: Reset the grid on connect here.
drawGrid(GRID_HAPPY);
sendGrid();

Prova

Ora, quando la pagina apre una connessione sul micro:bit, invierà una faccina felice. Se fai clic sulle caselle di controllo, il display viene mostrato sulla matrice LED.

  1. Ricarica la pagina.
  2. Fai clic sul pulsante Connetti.
  3. Nella finestra di dialogo del selettore della porta seriale, seleziona il dispositivo micro:bit BBC e fai clic su Connetti.
  4. Dovresti vedere un sorriso sulla matrice LED micro:bit.
  5. Disegna un pattern diverso sulla matrice LED modificando le caselle di controllo.

Aggiungi un evento di visualizzazione sui pulsanti micro:bit

Sulla scheda LED sono presenti due pulsanti, uno su entrambi i lati. Espruino fornisce una funzione setWatch che invia un evento/chiamata quando viene premuto il pulsante. Poiché vogliamo ascoltare entrambi i pulsanti, renderemo la nostra funzione generica e gli mostreremo 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);

Quindi, dobbiamo collegare entrambi i pulsanti (denominati BTN1 e BTN2 sulla scheda micro:bit) ogni volta che la porta seriale è collegata al dispositivo.

script.js - clickConnect()

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

Prova

Oltre a mostrare un volto felice quando è connesso, premi uno dei pulsanti sul micro:bit per aggiungere testo alla pagina che indica il pulsante premuto. Molto probabilmente, ogni carattere si trova su una riga separata.

  1. Ricarica la pagina.
  2. Fai clic sul pulsante Connetti.
  3. Nella finestra di dialogo del selettore della porta seriale, seleziona il dispositivo micro:bit BBC e fai clic su Connetti.
  4. Dovresti vedere un sorriso sulla matrice LED micro:bit.
  5. Premi i pulsanti sul micro:bit e verifica che aggiunga nuovo testo alla pagina con i dettagli del pulsante premuto.

Gestione di base dello stream

Quando viene eseguito il push di uno dei pulsanti micro:bit, il micro:bit invia i dati alla porta seriale tramite uno stream. I flussi di dati sono molto utili, ma possono anche essere una sfida, perché non devi necessariamente recuperare tutti i dati in una sola volta e il blocco potrebbe essere arbitrario.

L'app attualmente stampa lo stream in arrivo man mano che arriva (in readLoop). Nella maggior parte dei casi, ogni carattere è su una riga separata, ma questo non è molto utile. Idealmente, il flusso deve essere analizzato in singole righe e ogni messaggio viene visualizzato su una riga separata.

Trasformare gli stream con TransformStream

Per farlo, possiamo utilizzare un flusso di trasformazione (TransformStream), che consente di analizzare il flusso in entrata e restituire i dati analizzati. Un flusso di trasformazione può trovarsi tra la sorgente stream (in questo caso il micro:bit) e qualsiasi elemento che consuma il flusso (in questo caso readLoop) e può applicare una trasformazione arbitraria prima che venga finalmente consumata. Pensa a una catena di montaggio: quando un widget scende lungo la linea, ogni passaggio della riga modifica il widget, in modo che quando arriva alla sua destinazione finale, è un widget completamente funzionante.

Per ulteriori informazioni, consulta la sezione Concetti relativi all'API Streams di MDN's.

Trasforma il flusso con LineBreakTransformer

Creiamo una classe LineBreakTransformer, che inserirà uno stream e lo dividerà in base alle interruzioni di riga (\r\n). Sono necessari due metodi: transform e flush. Il metodo transform viene chiamato ogni volta che vengono ricevuti nuovi dati dallo stream. che può accodare i dati o salvarli per un secondo momento. Il metodo flush viene chiamato quando lo stream è chiuso e gestisce tutti i dati che non sono stati ancora elaborati.

Nel nostro metodo transform, aggiungeremo nuovi dati a container e verificheremo se sono presenti interruzioni di riga in container. In caso affermativo, suddividilo in un array e ripeti le righe, chiamando 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 è chiuso, scaricheremo semplicemente i dati rimanenti nel container utilizzando enqueue.

script.js - LineBreakTransformer.flush()

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

Infine, dobbiamo canalizzare lo stream in arrivo attraverso il nuovo LineBreakTransformer. Il nostro flusso di input originale era collegato tramite TextDecoderStream, quindi dobbiamo aggiungere un ulteriore pipeThrough per farlo arrivare 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 micro:bit, i dati stampati devono essere restituiti in un'unica riga.

  1. Ricarica la pagina.
  2. Fai clic sul pulsante Connetti.
  3. Nella finestra di dialogo del selettore della porta seriale, seleziona il dispositivo micro:bit BBC e fai clic su Connetti.
  4. Dovresti vedere un sorriso sulla matrice LED micro:bit.
  5. Premi i pulsanti sul micro:bit e verifica che siano visualizzati i seguenti elementi:

Trasforma il flusso con JSONTransformer

Abbiamo provato ad analizzare la stringa in formato JSON in readLoop, ma creiamo un trasformatore JSON molto semplice in grado di trasformare i dati in un oggetto JSON. Se i dati non sono JSON validi, restituisci semplicemente il contenuto.

script.js - JSONTransformer.transform

// CODELAB: Attempt to parse JSON content
try {
  controller.enqueue(JSON.parse(chunk));
} catch (e) {
  controller.enqueue(chunk);
}

Dopodiché, concludi il flusso attraverso il JSONTransformer, dopo che ha superato il LineBreakTransformer. In questo modo possiamo mantenere il semplice JSONTransformer, dato che sappiamo che il codice JSON verrà inviato solo su una singola 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 micro:bit, dovresti vedere [object Object] stampato.

  1. Ricarica la pagina.
  2. Fai clic sul pulsante Connetti.
  3. Nella finestra di dialogo del selettore della porta seriale, seleziona il dispositivo micro:bit BBC e fai clic su Connetti.
  4. Dovresti vedere un sorriso sulla matrice LED micro:bit.
  5. Premi i pulsanti sul micro:bit e verifica che siano visualizzati i seguenti elementi:

Risposta alle pressioni dei pulsanti

Per rispondere alle pressioni dei pulsanti micro:bit, aggiorna readLoop per verificare se i dati che ha ricevuto sono object con una proprietà button. Quindi, chiama buttonPushed per gestire il push 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 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 micro:bit, la matrice dei LED dovrebbe diventare una faccina felice o una faccina triste.

  1. Ricarica la pagina.
  2. Fai clic sul pulsante Connetti.
  3. Nella finestra di dialogo del selettore della porta seriale, seleziona il dispositivo micro:bit BBC e fai clic su Connetti.
  4. Dovresti vedere un sorriso sulla matrice LED micro:bit.
  5. Premi i pulsanti sul micro:bit e verifica che la matrice dei LED cambi.

Il passaggio finale consiste nel collegare la funzionalità di disconnessione per chiudere la porta quando l'utente è pronto.

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 l'interfaccia utente 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, quindi lo stream di output e la porta. Per chiudere lo stream di input, chiama reader.cancel(). La chiamata al numero 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;
  reader = null;
  inputDone = null;
}

Per chiudere lo stream di output, ottieni un writer, chiama close() e attendi la chiusura dell'oggetto outputDone:

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 si chiuda:

script.js - disconnect()

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

Prova

Ora puoi aprire e chiudere la porta seriale come preferisci.

  1. Ricarica la pagina.
  2. Fai clic sul pulsante Connetti.
  3. Nella finestra di dialogo del selettore della porta seriale, seleziona il dispositivo micro:bit BBC e fai clic su Connetti.
  4. Dovresti vedere un sorriso sulla matrice LED micro:bit
  5. Premi il pulsante Disconnetti e verifica che la matrice dei LED si disattivi e che non ci siano errori nella console.

Complimenti! Hai creato la tua prima applicazione 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 tutte le altre nuove fantastiche funzionalità web su cui sta lavorando il team di Chrome.