Веб давно научился работать с камерой, микрофоном и файлами, а вот железо на столе оставалось за бортом: плата с датчиками, лабораторный прибор, самодельный пульт требовали нативного приложения. WebUSB закрыл этот разрыв и позволил обычной странице говорить с USB-устройствами напрямую, без установщиков и магазинов приложений. Обновление прошивки, панель показаний датчиков, конфигуратор железа теперь живут в браузерной вкладке.
Механика доступа строгая: страница не может молча тронуть подключённое устройство, пользователь сам выбирает железо в системном окне, и согласие запоминается до отзыва. Ниже разобран весь путь: как запросить устройство, открыть его, захватить интерфейс, обменяться байтами и отреагировать на подключение и отключение, с кодом, ограничениями и границами поддержки.
Что такое WebUSB и какие двери он открывает веб-приложению
WebUSB это программный интерфейс браузера, который открывает странице доступ к универсальной последовательной шине. Страница получает объект устройства с методами открытия, настройки и обмена байтами, а браузер выступает посредником: он проверяет контекст безопасности, требует жест пользователя и следит, чтобы приложение не вышло за рамки разрешённого.
Сценарии применения собраны вокруг железа, которому нужен компьютер лишь как экран и кнопка. Приборная панель для платы с датчиками, утилита обновления прошивки, конфигуратор 3D-принтера, управление лабораторным прибором, интерактивный музейный стенд, кассовое периферийное оборудование. Раньше каждый такой случай означал нативное приложение под три операционные системы, теперь это одна веб-страница, которую обновили ночью и утром все пользователи получили свежую версию.
Ключевое свойство WebUSB в том, что он говорит с устройством на его родном языке. Не через драйвер операционной системы, а напрямую по протоколу, который описало само железо: команды прошивки, кадры телеметрии, запросы состояния. Отсюда и главное требование: устройство должно уметь этот язык, то есть его прошивка обязана сотрудничать с веб-приложением, а не рассчитывать на системный драйвер.
Ограничение стоит проговорить сразу: WebUSB не открывает произвольный порт USB, он работает только с теми устройствами, чьи интерфейсы не заняты операционной системой, и только после явного выбора пользователем. Идея скрытого доступа к любому железу отвергалась спецификацией с самого начала, и это решение делает технологию безопасной для пользователя и удобной для разработчика.
Согласие пользователя requestDevice и список разрешённых устройств
Точка входа это метод requestDevice, который обязан вызываться из обработчика пользовательского действия: клика или касания. Метод открывает системное окно со списком подключённого железа, человек выбирает устройство, и браузер связывает выбранный экземпляр с источником страницы. Фильтр сужает список до нужного производителя и модели по идентификаторам поставщика и продукта, чтобы человек не листал десятки лишних строк.
Согласие живёт дольше одной сессии. Метод getDevices возвращает все устройства, доступ к которым страница уже получала, и это позволяет восстанавливать соединение после перезагрузки страницы без нового системного окна. Разумная схема для приложений панели: при старте страница проверяет getDevices, работает с уже знакомым железом, а системное окно показывает только новому пользователю.
Отзыв согласия тоже предусмотрен: страница может вызвать forget и отпустить устройство, пользователь может сделать то же из настроек браузера. Приложение обязано переживать оба случая без падений: разорванное соединение это штатная ситуация, а не авария, и экран должен предлагать переподключение, а не стек ошибок.
Окно выбора показывает производителя, продукт и серийный номер, поэтому честная стратегия фильтра проста: указывать идентификатор своего железа, чтобы в списке не висели чужие устройства. Чем меньше лишнего в системном окне, тем быстрее человек дойдёт до кнопки выбора и тем меньше поводов для тревоги.
Основы шины USB которые стоит знать перед первым обменом байтами
USB устроен как набор конечных точек, через которые устройство и хост обмениваются байтами. Устройство описывает конфигурации, внутри конфигурации интерфейсы, внутри интерфейсов конечные точки с номерами и направлениями. Страница перед обменом обязана выбрать конфигурацию и захватить интерфейс, и только потом конечные точки этого интерфейса открываются для чтения и записи.
Направления зашиты в номера точек: входящие точки отдают байты от устройства к компьютеру, исходящие принимают байты от компьютера к устройству. Метод transferIn читает из входящей точки, transferOut пишет в исходящую, и оба возвращают обещание с отчётом о состоянии, поэтому код читается линейно, без обратных вызовов и гонок.
Помимо обменов есть управляющие передачи: канал нулевой точки всегда доступен и служит для запросов описателей и специфических команд устройства. На практике страница читает описание устройства, узнаёт доступные конфигурации и интерфейсы, и это же описание подсказывает номера точек, которые стоит использовать в обменах.
Каждая операция небыстрая по меркам процессора и быстрая по меркам человека: обмен занимает миллисекунды, и вкладка переживает отключение шнура как обычное отклонение обещания. Здоровая обработка ошибок не отличается от сетевой: повтор, сообщение человеку, предложение переподключить железо.
Открытие устройства выбор конфигурации и захват интерфейса в коде
Открытие устройства это три шага: open переводит его в рабочее состояние, selectConfiguration активирует одну из описанных конфигураций, claimInterface занимает интерфейс за страницей. Захват нужен потому, что интерфейс не может обслуживаться двумя приложениями одновременно, и браузер честно отказывает второму.
const device = await navigator.usb.requestDevice({
filters: [{ vendorId: 0x2341 }]
});
await device.open();
await device.selectConfiguration(1);
await device.claimInterface(0);
После захвата интерфейс можно отпустить методом releaseInterface, а всё устройство закрыть методом close и затем снова открыть: перезакрытие обходится дешевле повторного запроса согласия, потому что разрешение уже живёт в браузере. Порядок операций важен: попытка обмена до захвата интерфейса закончится отказом с понятным сообщением.
Номера конфигурации и интерфейса обычно известны заранее, потому что автор страницы читал описание своего железа. У устройств с несколькими конфигурациями выбор влияет на энергопотребление и доступные функции, и в вебе почти всегда работает первая конфигурация, оставленная прошивкой по умолчанию.
Обмен байтами transferIn transferOut и события подключения отключения
Обмен строится на паре методов. transferOut принимает номер исходящей точки и байты в виде типизированного массива, transferIn принимает номер входящей точки и размер пакета, а в ответе приносит объект с представлением принятых байтов и статусом операции. Кодировка текста в байты и обратно ложится на типовые кодировщики языка.
await device.transferOut(1, new TextEncoder().encode("HELLO"));
const result = await device.transferIn(1, 64);
const text = new TextDecoder().decode(result.data);
Размер пакета для transferIn подсказывает само устройство в описании конечной точки: типовое значение 64 байта для плат начального уровня, у скоростных желез больше. Чтение по кругу собирается в цикл, который засыпает между опросами или живёт на прерываниях от события, и вторая схема экономнее: страница просыпается только когда есть что прочитать.
navigator.usb.addEventListener("connect", (e) => {
console.log("attached:", e.device.productName);
});
navigator.usb.addEventListener("disconnect", (e) => {
console.log("detached:", e.device.productName);
});
События connect и disconnect дают странице реакцию на физические действия человека: воткнул шнур, панель ожила, вытащил, панель честно показала ожидание. Приложение, которое держит эти события в центре внимания, ощущается как нативное, потому что отвечает на реальность, а не на собственный внутренний цикл.
Поддержка браузеров границы возможностей и случаи где WebUSB не помощник
Поддержка сосредоточена в браузерах на движке Chromium: Chrome, Edge и их родственники. Firefox и Safari годами держат позицию невмешательства, и автору веб-приложения остаётся честно проверять navigator.usb и предлагать пользователю путь через поддерживаемый браузер, вместо того чтобы молча показывать пустой экран.
Самая частая путаница новичков это USB-переходники к последовательному порту. Дешёвые платы с чипами-преобразователями не работают с WebUSB, потому что их интерфейс занимается драйвером операционной системы и отображается как виртуальный последовательный порт. Для таких случаев существует отдельный интерфейс браузера для последовательного порта, и это другой разговор с другим согласием.
Второй случай это чужие устройства с закрытыми протоколами: формально страница может обменяться байтами, но без документации на команды прошивки разговор бессмысленный. WebUSB силён там, где автор контролирует и железо, и веб-приложение, а взлом чужих гаджетов в его цели не входит, да и окно выбора пользователя стоит на пути таких затей.
Третья граница это производительность: шина и браузер вводят накладные расходы, и поток в мегабайты в секунду остаётся достижимым, а вот попытки выжать предельные скорости шины упираются в планировщики. Для панели датчиков, конфигуратора и телеметрии этого более чем достаточно.
Практика интерактивных приложений от приборной панели до музейного стенда
Типовое интерактивное приложение на WebUSB собирается из трёх слоёв: слой связи с устройством, слой состояния с конечным автоматом соединения и слой интерфейса. Конечный автомат переживает все жизненные ситуации: ожидание выбора, подключение, работа, потеря связи, восстановление. Простой список состояний спасает от спагетти из флагов, которые иначе расползаются по всему коду.
Музейный стенд и выставочный макет это отдельный жанр: страница работает в полноэкранном режиме, устройство опрашивается постоянно, а интерфейс обязан переживать выдёргивание шнура посетителем. Проверенное решение держать восстановление на событиях disconnect и connect: страница никогда не падает, она просто ждёт, когда железо вернётся на место.
Обновление прошивки через браузер стало знаковой возможностью: производитель железа выкладывает страницу, человек подключает устройство, выбирает файл, и обмен идёт без установки утилиты. Для производителей это экономия на поддержке трёх платформ, для пользователей отсутствие программ, которые ставятся ради одной операции и потом висят в системе годами.
Полевые испытания обязательны: шнуры разной длины, порты разных материнских плат, режимы сна ноутбука и выдёргивание на середине обмена. Список таких проверок копится быстро и превращается в чек-лист релиза, а находки из него стоят дороже десятка юнит-тестов, потому что воспроизводят реальные руки реальных людей.
Порядок сборки веб-приложения с WebUSB свёрнут в короткую лестницу:
- проверить navigator.usb и поддерживаемый браузер на входе;
- вызвать requestDevice по клику с фильтром по производителю;
- открыть устройство, выбрать конфигурацию и захватить интерфейс;
- обменяться байтами через transferIn и transferOut по протоколу железа;
- подписаться на события connect и disconnect и построить восстановление;
- обработать отказы доступа как штатные состояния интерфейса;
- хранить протокол обмена в одном модуле, чтобы его легко было обновлять.
WebUSB вернул веб-разработке кусок мира, который двадцать лет считался территорией нативных утилит: железо на столе теперь разговаривает со страницей напрямую. Команда, у которой есть и плата, и браузер, собирает интерактивный продукт силами одних и тех же людей, и граница между вебом и устройством становится просто ещё одним интерфейсом в кодовой базе.