Premiers pas avec l'API Web Serial

Dernière mise à jour : 08/11/2019

Objectifs de l'atelier

Dans cet atelier de programmation, vous allez créer une page Web qui utilise l'API Web Serial pour interagir avec une carte BBC:bit afin d'afficher des images sur sa matrice LED 5x5. Vous découvrirez l'API Web Serial et apprendrez à utiliser des flux lisibles, accessibles en écriture et à transformer pour communiquer avec des appareils sériels via le navigateur.

Points abordés

  • Ouvrir et fermer un port Web Serial
  • Gérer les données d'un flux d'entrée à l'aide d'une boucle de lecture
  • Envoyer des données via un flux d'écriture

Prérequis

Nous avons choisi d'utiliser le micro-bit pour cet atelier de programmation, car il est abordable, propose quelques entrées (boutons) et sorties (affichage LED 5 x 5), et peut fournir des entrées et des sorties supplémentaires. Consultez la page concernant le micro-bit BBC sur le site Espruino pour en savoir plus sur les fonctionnalités du micro-bit :

L'API Web Serial fournit aux sites Web un moyen de lire et d'écrire sur un appareil sériel à l'aide de scripts. L'API relie le Web et le monde physique en permettant aux sites Web de communiquer avec des appareils sériels, tels que des microcontrôleurs et des imprimantes 3D.

Il existe de nombreux exemples de logiciels de contrôle développés à l'aide de la technologie Web. Exemple :

Dans certains cas, ces sites Web communiquent avec l'appareil via une application d'agent native installée manuellement par l'utilisateur. Dans d'autres cas, l'application est diffusée dans une application native empaquetée via un framework tel qu'Electron. Dans d'autres, l'utilisateur doit réaliser une autre étape, comme copier une application compilée sur l'appareil à l'aide d'une clé USB.

Il peut être amélioré en proposant une communication directe entre le site et l'appareil qu'il contrôle.

Activer l'API Web Serial

L'API Web Serial est en cours de développement et n'est disponible que derrière un drapeau. Vous devez activer l'indicateur #enable-experimental-web-platform-features dans chrome://flags.

Récupérer le code

Nous avons rassemblé tout ce dont vous avez besoin dans cet atelier de programmation dans un projet Glitch.

  1. Ouvrez un nouvel onglet dans votre navigateur pour accéder à l'adresse https://web-series-codelab-start.glitch.me/.
  2. Cliquez sur le lien Remix Glitch pour créer votre propre version du projet initial.
  3. Cliquez sur le bouton Afficher, puis sélectionnez Dans une nouvelle fenêtre pour voir votre code en action.

Vérifier si l'API Web Serial est compatible

La première chose à faire est de vérifier si l'API Web Serial est compatible avec le navigateur actuel. Pour ce faire, vérifiez si serial se trouve dans navigator.

Dans l'événement DOMContentLoaded, ajoutez le code suivant à votre projet:

script.js - DOMContentLoaded

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

Cet article vérifie si le Web Serial est compatible. Si c'est le cas, ce code masque la bannière indiquant que Web Serial n'est pas compatible.

Essayer

  1. Chargez la page.
  2. Vérifiez que la page ne comporte pas de bannière rouge indiquant que Web Serial n'est pas compatible.

Ouvrir le port série

Nous devons ensuite ouvrir le port série. Comme la plupart des autres API modernes, l'API Web Serial est asynchrone. Cela empêche le blocage de l'interface utilisateur lors de l'attente, mais c'est également important, car des données série peuvent être reçues à tout moment par la page Web, et nous devons les écouter.

Étant donné qu'un ordinateur peut disposer de plusieurs appareils sériels, lorsque le navigateur tente de demander un port, l'utilisateur est invité à choisir l'appareil auquel se connecter.

Ajoutez le code suivant à votre projet:

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 });

L'appel requestPort invite l'utilisateur à quel appareil il souhaite se connecter. Le transfert de port.open ouvre le port. Nous devons également fournir la vitesse à laquelle nous souhaitons communiquer avec l'appareil série. Le micro-bit BBC utilise une connexion 9600 bauds entre la puce USB vers série et le processeur principal.

Associez le bouton de connexion et demandez-lui d'appeler connect() lorsque l'utilisateur clique dessus.

Ajoutez le code suivant à votre projet:

script.js - clickConnect()

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

Essayer

Dans un premier temps, notre projet dispose des fonctionnalités minimales requises. Lorsque l'utilisateur clique sur le bouton Connecter, il sélectionne l'appareil série auquel se connecter, puis le micro:bit.

  1. Actualisez la page.
  2. Cliquez sur le bouton Connexion.
  3. Dans la boîte de dialogue du sélecteur de port série, sélectionnez l'appareil BBC micro-bit et cliquez sur Connect (Connecter).
  4. Dans l'onglet, vous devriez voir une icône indiquant que vous vous êtes connecté à un appareil sériel.

Configurer un flux d'entrée pour écouter les données du port série

Une fois la connexion établie, nous devons configurer un flux d'entrée et un lecteur pour lire les données de l'appareil. Tout d'abord, nous allons récupérer le flux lisible depuis le port en appelant port.readable. Nous savons que nous allons récupérer le texte stocké sur l'appareil. C'est pourquoi nous l'acheminerons via un décodeur. Ensuite, nous allons obtenir un lecteur et lancer la boucle de lecture.

Ajoutez le code suivant à votre projet:

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();

La boucle de lecture est une fonction asynchrone qui s'exécute en boucle et attend le contenu sans bloquer le thread principal. Lorsque de nouvelles données arrivent, le lecteur renvoie deux propriétés: la propriété value et la valeur booléenne done. Si la valeur de done est"true", le port a été fermé ou il n'y a plus de données à venir.

Ajoutez le code suivant à votre projet:

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;
  }
}

Essayer

Le projet peut maintenant se connecter à l'appareil et ajouter à celui-ci toutes les données reçues depuis l'appareil.

  1. Actualisez la page.
  2. Cliquez sur le bouton Connexion.
  3. Dans la boîte de dialogue du sélecteur de port série, sélectionnez l'appareil BBC micro-bit et cliquez sur Connect (Connecter).
  4. Le logo Espruino doit s'afficher:

Configurer un flux de sortie pour envoyer des données au port série

La communication série est généralement bidirectionnelle. En plus de recevoir les données du port série, nous souhaitons également envoyer des données vers le port. Comme pour le flux d'entrée, nous n'envoyons du texte que sur le flux de sortie au micro:bit.

Commencez par créer un flux d'encodeur de texte et ajoutez-le à 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;

Lorsqu'il est connecté en série avec le micrologiciel Espruino, le tableau de micro-octets BBC, qui agit en tant que boucle de lecture en lecture (REL) JavaScript, est semblable à celui fourni dans une interface système Node.js. Nous devons ensuite fournir une méthode pour envoyer des données au flux. Le code ci-dessous extrait un rédacteur du flux de sortie, puis utilise write pour envoyer chaque ligne. Chaque ligne envoyée comprend un caractère de ligne supplémentaire (\n), qui indique au micro:bit d'évaluer la commande envoyée.

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();

Pour définir le système dans un état connu et l'empêcher d'échouer les caractères que nous lui envoyons, nous devons envoyer un CTRL-C et désactiver l'écho.

script.js - connect()

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

Essayer

Notre projet peut désormais envoyer et recevoir des données à partir du micro:bit. Vérifions que nous pouvons envoyer une commande:

  1. Actualisez la page.
  2. Cliquez sur le bouton Connexion.
  3. Dans la boîte de dialogue du sélecteur de port série, sélectionnez l'appareil BBC micro-bit et cliquez sur Connect (Connecter).
  4. Ouvrez l'onglet Console dans les outils pour les développeurs Chrome, puis saisissez
    writeToStream('console.log("yes")');.

Le résultat devrait ressembler à ceci:

Créer la chaîne de la matrice de grille

Pour contrôler la matrice LED sur le micro-bit, nous devons appeler show(). Cette méthode permet d'afficher des images sur l'écran LED 5x5 intégré. Cette opération utilise un nombre binaire ou une chaîne.

Nous allons itérer sur les cases à cocher et générer un tableau de 1 s et 0 s, qui indique qui est coché et laquelle n'est pas. Nous devons ensuite inverser le tableau, car l'ordre des cases à cocher est inverse de l'ordre des LED dans la matrice. Nous convertirons ensuite le tableau en chaîne et créerons la commande à envoyer au 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('')})`);

Associer des cases à cocher pour mettre à jour la matrice

Ensuite, soyez attentif aux modifications apportées aux cases à cocher, puis envoyez ces informations au micro-bit (si elles sont modifiées). Dans le code de détection de caractéristiques (// CODELAB: Add feature detection here.), ajoutez la ligne suivante:

script.js - DOMContentLoaded

initCheckboxes();

Nous réinitialisons également la grille lors de la première connexion du micro:bit de sorte qu'il affiche un visage souriant. La fonction drawGrid() est déjà fournie. Cette fonction fonctionne de la même manière que sendGrid(), et utilise un tableau de 1 s et 0 s, puis coche les cases correspondantes.

script.js - clickConnect()

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

Essayer

Désormais, lorsque la page ouvrira une connexion au micro-bit, elle enverra un visage souriant. Si vous cochez les cases, l'affichage est affiché sur la matrice LED.

  1. Actualisez la page.
  2. Cliquez sur le bouton Connexion.
  3. Dans la boîte de dialogue du sélecteur de port série, sélectionnez l'appareil BBC micro-bit et cliquez sur Connect (Connecter).
  4. Vous devriez voir un sourire sur la matrice LED micro-bit :
  5. Dessinez un autre modèle sur la matrice à LED en cochant les cases.

Ajouter un événement de lecture sur les boutons des micro-bits

Il y a deux boutons sur le micro:bit, un de chaque côté de la matrice LED. Espruino fournit une fonction setWatch qui envoie un événement/rappel lorsque l'utilisateur appuie sur le bouton. Comme nous voulons écouter les deux boutons, nous allons rendre notre fonction générique et l'imprimer pour les détails de l'événement.

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);

Ensuite, nous devons brancher les deux boutons (nommés BTN1 et BTN2 sur la carte micro-bit) chaque fois que le port série est connecté à l'appareil.

script.js - clickConnect()

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

Essayer

En plus d'afficher un visage souriant lorsqu'il est connecté, l'un des boutons du micro:bit ajoute du texte à la page indiquant le bouton en question. Chaque caractère se trouve probablement sur une ligne distincte.

  1. Actualisez la page.
  2. Cliquez sur le bouton Connexion.
  3. Dans la boîte de dialogue du sélecteur de port série, sélectionnez l'appareil BBC micro-bit et cliquez sur Connect (Connecter).
  4. Vous devriez voir un smiley sur la matrice LED micro-bits.
  5. Appuyez sur les boutons du micro:bit et vérifiez qu'il ajoute du texte à la page avec les détails du bouton appuyé.

Gestion de base des flux

Lorsque l'un des boutons "micro:bit" est enclenché, ce dernier envoie les données au port série via un flux. Les flux sont très utiles, mais ils peuvent également constituer un véritable défi, car il n'est pas nécessairement nécessaire de rassembler toutes les données en même temps. De plus, celles-ci peuvent être arbitrairement divisées.

L'application affiche actuellement le flux entrant à son arrivée (en readLoop). Dans la plupart des cas, chaque caractère se trouve sur sa propre ligne, mais cela n'est pas très utile. Idéalement, le flux doit être analysé en lignes individuelles et chaque message doit être affiché sur sa propre ligne.

Transformer les flux avec TransformStream

Pour ce faire, nous pouvons utiliser un flux de transformation (TransformStream), ce qui permet d'analyser le flux entrant et de renvoyer les données analysées. Un flux de transformation peut se trouver entre la source du flux (dans le cas présent, micro-bit) et tout ce qui consomme le flux (dans le cas présent, readLoop). Il peut appliquer une transformation arbitraire avant qu'elle ne soit utilisée. Il s'agit, en quelque sorte, d'une ligne d'assemblage: à mesure que chaque widget avance sur la ligne, le widget le modifie de façon que, lorsqu'il atteint sa destination finale, il s'agit d'un widget entièrement fonctionnel.

Pour en savoir plus, consultez l'article Concepts de l'API Streams de MDN.

Transformer le flux avec LineBreakTransformer

Nous allons créer une classe LineBreakTransformer, qui utilisera un flux et le divisera en fonction des sauts de ligne (\r\n). La classe nécessite deux méthodes, transform et flush. La méthode transform est appelée à chaque fois que le flux reçoit de nouvelles données. Il peut mettre les données en file d'attente ou les enregistrer pour plus tard. La méthode flush est appelée lorsque le flux est fermé et gère toutes les données qui n'ont pas encore été traitées.

Dans notre méthode transform, nous ajoutons des données à container, puis nous vérifions si des sauts de ligne sont présents dans container. Le cas échéant, divisez-le en tableau, puis effectuez une itération sur les lignes, en appelant controller.enqueue() pour envoyer les lignes analysées.

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));

Lorsque le flux est fermé, nous vidons simplement toutes les données restantes dans le conteneur en utilisant enqueue.

script.js - LineBreakTransformer.flush()

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

Enfin, nous devons canaliser le flux entrant via le nouveau LineBreakTransformer. Notre flux d'entrée d'origine n'a été redirigé que via un TextDecoderStream. Nous devons donc ajouter un pipeThrough supplémentaire via le nouveau 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()));

Essayer

Désormais, lorsque vous appuyez sur l'un des boutons micro-bit:les données imprimées doivent être renvoyées sur une seule ligne.

  1. Actualisez la page.
  2. Cliquez sur le bouton Connexion.
  3. Dans la boîte de dialogue du sélecteur de port série, sélectionnez l'appareil BBC micro-bit et cliquez sur Connect (Connecter).
  4. Vous devriez voir un sourire sur la matrice LED micro-bit :
  5. Appuyez sur les boutons du micro-bit et vérifiez que vous obtenez un écran semblable à celui-ci :

Transformer le flux avec JSONTransformer

Nous pouvons essayer d'analyser la chaîne au format JSON dans readLoop. À la place, nous allons créer un transformateur JSON très simple qui transformera les données en un objet JSON. Si les données ne sont pas au format JSON valide, il suffit de renvoyer ce qui est arrivé.

script.js - JSONTransformer.transform

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

Ensuite, acheminer le flux à travers le JSONTransformer, après qu'il est passé par le LineBreakTransformer. Cela permet de simplifier notre JSONTransformer, car nous savons que le JSON ne sera envoyé que sur une seule ligne.

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()));

Essayer

Désormais, lorsque vous appuyez sur l'un des boutons micro-bit:[object Object] devrait s'afficher sur la page.

  1. Actualisez la page.
  2. Cliquez sur le bouton Connexion.
  3. Dans la boîte de dialogue du sélecteur de port série, sélectionnez l'appareil BBC micro-bit et cliquez sur Connect (Connecter).
  4. Vous devriez voir un sourire sur la matrice LED micro-bit :
  5. Appuyez sur les boutons du micro-bit et vérifiez que la commande suivante s'affiche :

Répondre aux pressions sur les boutons

Pour répondre aux pressions sur le bouton micro-bit, mettez à jour le readLoop afin de vérifier que les données reçues sont bien du type object avec une propriété button. Appelez ensuite buttonPushed pour gérer la transmission des boutons.

script.js - readLoop()

const { value, done } = await reader.read();
if (value && value.button) {
  buttonPushed(value);
} else {
  log.textContent += value + '\n';
}

Lorsqu'un bouton micro-bit est enfoncé, l'affichage de la matrice LED devrait changer. Utilisez le code suivant pour définir 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();
  }
}

Essayer

Désormais, lorsque vous appuyez sur l'un des boutons micro-bit:la matrice LED doit s'afficher comme un visage heureux ou triste.

  1. Actualisez la page.
  2. Cliquez sur le bouton Connexion.
  3. Dans la boîte de dialogue du sélecteur de port série, sélectionnez l'appareil BBC micro-bit et cliquez sur Connect (Connecter).
  4. Vous devriez voir un smiley sur la matrice LED micro-bits.
  5. Appuyez sur les boutons du micro:bit et vérifiez que la matrice LED change.

La dernière étape consiste à associer la fonctionnalité de déconnexion afin de fermer le port lorsque l'opération est terminée.

Fermez le port lorsque l'utilisateur clique sur le bouton "Connect/Déconnecter"

Lorsque l'utilisateur clique sur le bouton Connecter/Déconnecter, nous devons fermer la connexion. Si le port est déjà ouvert, appelez disconnect() et mettez à jour l'interface utilisateur pour indiquer que la page n'est plus connectée à l'appareil série.

script.js - clickConnect()

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

Fermer les flux et le port

Dans la fonction disconnect, nous devons fermer le flux d'entrée, le flux de sortie, puis le port. Pour fermer le flux d'entrée, appelez reader.cancel(). L'appel de cancel est asynchrone. Nous devons donc utiliser await pour qu'il se termine:

script.js - disconnect()

// CODELAB: Close the input stream (reader).
if (reader) {
  await reader.cancel();
  await inputDone;
  reader = null;
  inputDone = null;
}

Pour fermer le flux de sortie, obtenez un objet writer, appelez close() et attendez que l'objet outputDone soit fermé:

script.js - disconnect()

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

Enfin, fermez le port série et attendez la fermeture:

script.js - disconnect()

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

Essayer

Vous pouvez maintenant ouvrir et fermer le port série comme bon vous semble.

  1. Actualisez la page.
  2. Cliquez sur le bouton Connexion.
  3. Dans la boîte de dialogue du sélecteur de port série, sélectionnez le micro-bit BBC, puis cliquez sur Connect (Connecter).
  4. Vous devriez voir un smiley sur la matrice LED micro-bit :
  5. Appuyez sur le bouton Déconnecter et vérifiez que la matrice LED s'éteint et que la console ne comporte pas d'erreurs.

Félicitations ! Vous avez créé votre première application Web qui utilise l'API Web Serial.

Consultez régulièrement la page https://goo.gle/fugu-api-tracker pour découvrir les dernières nouveautés de l'API Web Serial et toutes les autres nouvelles fonctionnalités Web fantastiques sur lesquelles l'équipe Chrome travaille.