Web Serial Api podstawy

Przykład komunikacji po porcie szeregowym

Przykład pochodzi z prezentacji JavaScript i IoT. Jak przy pomocy tego, co znasz zbudować coś namacalnego, którą przedstawiłem 15. października 2026 roku podczas wrocławskiego meet.js.

Web Serial API jest obecnie dobrze obsługiwane, w Chrome od wersji 89 a w Firefox od wersji 151 (Browser compatibility).

Narzędziownik
JavaScript
Obsługa Web Serial API
Micropython
Firmware dla Pico
Raspberry Pi Pico 2 W
Mikrokontroler

Firmware napisany w Micropython wypisuje na wyjściu pong jeżeli na wejściu dostaje ping. Jako mikrokontrolera użyłem Raspberry Pi Pico 2 W.

import sys

print('Hello')

while True:
    command = sys.stdin.readline().strip()
    if command == 'ping':
        print('pong')

Firmware wrzuciłem przy pomocy narzędzia mpremote (dokumentacja mpremote).

mpremote cp ./firmware.py :main.py + reset

Sprawdzamy listę podłączonych urządzeń i podłączamy się:

mpremote connect list
mpremote connect /dev/cu.usbmodem14401 # jeżeli port istnieje

Na wyjściu powinniśmy dostać:

Connected to MicroPython at /dev/cu.usbmodem14401
Use Ctrl-] or Ctrl-x to exit this shell

Hello

Wpiszmy ping, otrzymamy pong. Komunikacja po porcie szeregowym działa w dwie strony.

Connected to MicroPython at /dev/cu.usbmodem14401
Use Ctrl-] or Ctrl-x to exit this shell

Hello
ping
=> pong

A teraz spięcie z JavaScript

Specyfikacja mówi:

Wskazówka

Secure context: This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

Użyłem vite do zbudowania projektu. Najpierw całość kodu:

let port, writer;

function send(command) {
  // the firmware reads one command per line
  return writer.write(new TextEncoder().encode(`${command}\n`));
}

// split incoming text into lines, data can arrive in arbitrary chunks
function splitLines() {
  let buffer = "";

  return new TransformStream({
    transform(chunk, controller) {
      buffer += chunk;
      const lines = buffer.split("\n");
      buffer = lines.pop();
      lines.forEach((line) => controller.enqueue(line.trim()));
    },
  });
}

async function readLines() {
  const lines = port.readable
    .pipeThrough(new TextDecoderStream())
    .pipeThrough(splitLines());

  // Listen to data coming from the serial device.
  for await (const line of lines) {
    console.log(line);
  }
}

async function connect() {
  port = await navigator.serial.requestPort();

  await port.open({ baudRate: 115200 });
  writer = port.writable.getWriter();

  readLines();
  await send("ping");
}

function main() {
  if ("serial" in navigator) {
    document.querySelector("#web-serial-port-status").textContent =
      "Web Serial Port is supported";

    const connectButton = document.querySelector(".connect-button");
    connectButton.hidden = false;
    connectButton.addEventListener("click", connect);
  }
}

main();
function main() {
  if ("serial" in navigator) {
    document.querySelector("#web-serial-port-status").textContent =
      "Web Serial Port is supported";

    const connectButton = document.querySelector(".connect-button");
    connectButton.hidden = false;
    connectButton.addEventListener("click", connect);
  }
}

Jeżeli przeglądarka obsługuje Web Serial API połączenie z urządzeniem można wykonać dopiero po akcji użytkownika, np. kliknięciu w przycisk.

async function connect() {
  port = await navigator.serial.requestPort();

  await port.open({ baudRate: 115200 });
  writer = port.writable.getWriter();

  readLines();
  await send("ping");
}

Po kliknięciu powinniśmy dostać listę urządzeń. Wartość baudRate: 115200 (bps) jest ściśle związana z mikrokontrolerem. writer = port.writable.getWriter(); oraz port.readable kolejno pozwalają na zapisywanie i odczytywanie z urządzenia danych. await send("ping") dodaje na koniec polecenia znak nowej lini:

function send(command) {
  // the firmware reads one command per line
  return writer.write(new TextEncoder().encode(`${command}\n`));
}

Zdjęcia dzięki uprzejmości Cezarego Dynaka, Aleksandry Pawlus oraz Adriana Romańskiego.

Cześć! Jestem otwarty na nowe wyzwania zawodowe.

Brzmi interesująco? Skontaktuj się ze mną poprzez poprzez LinkedIn

Przed prezentacją. Na zdjęciu Arduino UNO, ESP32, Raspberry Pi Pico 2 W
Przed prezentacją. Na zdjęciu Arduino UNO, ESP32, Raspberry Pi Pico 2 W