Web Serial API: Прямий зв'язок браузера з залізом
Хочу трохи розповісти про WebSerial API. Мабуть, ви вже бачили, як деякі сайти дозволяють прошивати мікроконтролери прямо зі своєї сторінки. Вперше я зіткнувся з цим у дитячому конструкторі Micro:bit. Дитина може зібрати алгоритм у Scratch і одним натисканням кнопки завантажити прошивку на плату прямо з браузера.
Це надзвичайно зручно (допоки у вас є інтернет):
Не потрібно інсталювати спеціальний софт (IDE, драйвери чи консольні утиліти).
Жодних складних налаштувань чи конфігурацій середовища.
Мінімум проблем з правами доступу (хоча в Linux вони все ж можуть знадобитися).
Фактично, у вас є лише одна HTML + JavaScript сторінка, яка реалізує всю логіку. Це працює майже всюди, де є браузери на базі Chromium (Google Chrome, Microsoft Edge, Opera, Vivaldi, Brave). Доречі на мобільному Chrome під андроідом це метод дає доступ до Bluetooth/BLE serial, а ось USB CDC пристрої в мене не запрацювали.
Також варто зауважити, що серед популярних браузерів підтримка відсутня у Firefox та Safari. Розробники офіційно вважають цю технологію потенційно небезпечною, оскільки вона дає вебсторінкам прямий доступ до апаратного забезпечення комп’ютера.
Що нам знадобиться для старту:
Хостинг із підтримкою HTTPS. Це критично важлива вимога безпеки. WebSerial API (як і більшість сучасних Web APIs) працює виключно в захищеному контексті. Якщо ваш сайт працює через звичайний HTTP, браузер просто заблокує доступ до портів. Також можна використати локальний HTTP сервер (тобто http://localhost), він вважається безпечним за замовчуванням.
Мікроконтролер із підтримкою USB CDC. У моєму випадку це буде модуль з RP2040, який ідеально підходить для цієї задачі, оскільки підтримка CDC у нього вбудована на рівні заліза та стандартних бібліотек.
RP2040 HelloWorld
Почнемо з класичного прикладу, щоб перевірити зв’язок між залізом та браузером. При створенні проєкту в SDK важливо увімкнути опцію Console over USB. Це дозволяє перенаправити стандартний вивід stdio не в апаратний UART, а у віртуальний COM-порт через USB. І після цього функції printf/fread/fwrite будуть працювати з ним.
У файлі CMakeLists.txt за це відповідають наступні рядки:
pico_enable_stdio_usb(назва_проєкту 1) pico_enable_stdio_uart(назва_проєкту 0)
Код прошивки:
#include "pico/stdlib.h"
int main()
{
stdio_init_all();
while (true) {
printf("Hello, world %d!\n",
to_ms_since_boot(get_absolute_time())
);
sleep_ms(1000);
}
}Раз на секунду мікроконтролер надсилатиме тестовий рядок у USB-порт. Тепер наша задача — зловити його на стороні вебсторінки.
Вебчастина
У JavaScript насамперед необхідно перевірити підтримку WebSerial браузером. Робиться це так:
if (!('serial' in navigator)) {
alert('Web Serial API not supported in this browser.');
return;
}
}Наступний крок — підключення та відкриття порту. Оскільки методи
WebSerial API повертають Promise, їх потрібно викликати
за допомогою оператора await всередині асинхронних
функцій:
port = await navigator.serial.requestPort();
await port.open({ baudRate: 115200 });Для завершення роботи використовується метод
port.close().
Для отримання даних потрібно отримати об’єкт Reader. Оскільки потік даних є бінарним, reader повертає буфери (Uint8Array). Якщо ви очікуєте текст, ці дані необхідно декодувати, наприклад, за допомогою TextDecoder:
const textDecoder = new TextDecoder();
while (true) {
const { value, done } = await reader.read();
if (done) break;
console.log(textDecoder.decode(value));
}Повний приклад сторінки
Створимо просту сторінку з двома кнопками: Connect та Disconnect. Усі отримані дані будемо декодувати в текст та виводити в консоль браузера.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSerial Simple Connect</title>
</head>
<body>
<button id="connectBtn">Connect</button>
<button id="disconnectBtn">Disconnect</button>
<script>
var port = null;
var reader = null;
document.getElementById('connectBtn').onclick = async () => {
if (!('serial' in navigator)) {
alert('Web Serial API not supported in this browser.');
return;
}
port = await navigator.serial.requestPort();
await port.open({ baudRate: 115200 });
console.log('Connected to serial device');
reader = port.readable.getReader();
const textDecoder = new TextDecoder();
while (true) {
const { value, done } = await reader.read();
if (done) break;
console.log(textDecoder.decode(value));
}
};
document.getElementById('disconnectBtn').onclick = async () => {
console.log('Disconnecting from serial device');
if (reader) {
await reader.cancel();
reader = null;
}
if (port) {
await port.close();
port = null;
}
};
</script>
</body>
</html>При натисканні кнопки Connect з’являється діалогове вікно вибору пристрою:
У списку ми бачимо ttyACM0 — це і є мій RP2040 Pico.
Після підключення в консолі з’являється потік повідомлень:
Варто зауважити, що дані через послідовний порт можуть приходити частинами. Як видно на скріншоті, один рядок може бути розбитий на кілька окремих буферів. Обробку таких випадків (наприклад, склеювання рядків до символу переносу ) розробнику потрібно реалізовувати самостійно.
Висновки
WebSerial API — це надзвичайно цікава технологія. Вона дозволяє миттєво візуалізувати дані з мікроконтролера на великому екрані комп’ютера або навіть керувати залізом без інсталяції жодного спеціалізованого софту.
Де можна застосувати WebSerial:
Швидке створення дашбордів: якщо потрібно вивести графіки з сенсорів або логи дебагу в зручному веб-інтерфейсі.
Оновлення прошивок: чудово підійде для сервісних сторінок, де користувач може оновити пристрій у браузері.
Кроспалатформність: ваш код працюватиме на Windows, macOS та Linux без переписування під кожну ОС.


Коментарі
Дописати коментар