Реализация Web USB API на сайте

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация Web USB API на сайте
Сложная
~2-3 рабочих дня
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Реализация Web USB API на сайте

WebUSB API открывает доступ к USB-устройствам прямо из браузера — без драйверов, без нативного приложения. Принтеры этикеток, POS-терминалы, промышленные сканеры, Arduino-устройства, программаторы микроконтроллеров — всё это подключается к веб-интерфейсу напрямую.

Ограничения и реальность

Поддержка: Chrome/Edge 61+ (десктоп и Android). Safari и Firefox — не поддерживают.

Только HTTPS. Только user gesture. USB-устройства с активными ядерными USB-драйверами ОС (HID, масс-стораж, принтеры с системными драйверами) — недоступны через WebUSB: браузер не перехватывает их у ОС. Нужен специальный firmware с WebUSB дескрипторами, или устройство без автоматически устанавливаемого драйвера.

Запрос и подключение устройства

interface USBDeviceInfo {
  vendorId: number   // из документации производителя
  productId: number
}

async function requestUSBDevice(filters: USBDeviceInfo[]): Promise<USBDevice> {
  const device = await navigator.usb.requestDevice({ filters })
  return device
}

async function connectDevice(device: USBDevice): Promise<void> {
  await device.open()

  // Если устройство имеет несколько конфигураций — выбираем нужную
  if (device.configuration === null) {
    await device.selectConfiguration(1)
  }

  // Захватываем интерфейс (нужный номер — из документации или USB descriptor)
  await device.claimInterface(0)

  console.log(`Подключено: ${device.manufacturerName} ${device.productName}`)
  console.log(`USB ${device.usbVersionMajor}.${device.usbVersionMinor}`)
}

Принтер этикеток ZPL (Zebra/аналоги)

Принтеры Zebra используют ZPL (Zebra Programming Language). Команды отправляются как plain text через bulk transfer:

class ZebraPrinter {
  private device: USBDevice
  private interfaceNumber = 0
  private endpointOut = 1  // bulk OUT endpoint — из USB descriptor устройства

  constructor(device: USBDevice) {
    this.device = device
  }

  async print(zplCommands: string): Promise<void> {
    const encoder = new TextEncoder()
    const data = encoder.encode(zplCommands)

    const result = await this.device.transferOut(this.endpointOut, data)
    if (result.status !== 'ok') {
      throw new Error(`Print error: ${result.status}`)
    }
  }

  async printLabel(params: {
    barcode: string
    title: string
    price: string
    sku: string
  }): Promise<void> {
    // ZPL разметка этикетки 60x40mm
    const zpl = `
^XA
^CI28
^FO20,10^A0N,24,24^FD${params.title}^FS
^FO20,40^BY2^BCN,50,Y,N,N^FD${params.barcode}^FS
^FO20,100^A0N,20,20^FDSKU: ${params.sku}^FS
^FO20,125^A0N,28,28^FD${params.price}^FS
^XZ
    `.trim()

    await this.print(zpl)
  }

  async getStatus(): Promise<string> {
    // Host Status Command
    await this.print('~HS')

    // Читаем ответ с bulk IN endpoint
    const result = await this.device.transferIn(1, 64)  // endpoint IN, 64 bytes
    const decoder = new TextDecoder()
    return decoder.decode(result.data!)
  }
}

Arduino: двусторонний обмен данными

Arduino с заливкой CDC firmware работает как USB Serial. Но без CDC-драйвера ОС — нужен кастомный подход через WebUSB библиотеку для Arduino:

class ArduinoDevice {
  private device: USBDevice
  private interfaceNumber = 2
  private endpointIn = 5
  private endpointOut = 4
  private decoder = new TextDecoder()
  private encoder = new TextEncoder()

  private readBuffer = ''
  private isReading = false

  constructor(device: USBDevice) {
    this.device = device
  }

  async startReading(onData: (line: string) => void) {
    this.isReading = true

    while (this.isReading) {
      try {
        const result = await this.device.transferIn(this.endpointIn, 64)
        const chunk = this.decoder.decode(result.data!, { stream: true })
        this.readBuffer += chunk

        const lines = this.readBuffer.split('\n')
        this.readBuffer = lines.pop()!  // Последний элемент — неполная строка

        for (const line of lines) {
          if (line.trim()) onData(line.trim())
        }
      } catch (err) {
        if ((err as Error).name === 'NetworkError') break  // Устройство отключилось
        throw err
      }
    }
  }

  async sendCommand(command: string): Promise<void> {
    const data = this.encoder.encode(command + '\n')
    await this.device.transferOut(this.endpointOut, data)
  }

  stopReading() {
    this.isReading = false
  }
}

// Использование
const arduino = new ArduinoDevice(device)

arduino.startReading((line) => {
  // Парсим данные датчика: "TEMP:23.5,HUM:65.2"
  const match = line.match(/TEMP:([\d.]+),HUM:([\d.]+)/)
  if (match) {
    setSensorData({ temp: parseFloat(match[1]), humidity: parseFloat(match[2]) })
  }
})

await arduino.sendCommand('LED:ON')
await arduino.sendCommand('SERVO:90')

React-хук для WebUSB

function useWebUSB() {
  const [device, setDevice] = useState<USBDevice | null>(null)
  const [isConnected, setIsConnected] = useState(false)
  const [isSupported] = useState(() => 'usb' in navigator)

  useEffect(() => {
    if (!isSupported) return

    function onConnect(event: USBConnectionEvent) {
      console.log('USB подключено:', event.device.productName)
    }

    function onDisconnect(event: USBConnectionEvent) {
      if (event.device === device) {
        setDevice(null)
        setIsConnected(false)
      }
    }

    navigator.usb.addEventListener('connect', onConnect)
    navigator.usb.addEventListener('disconnect', onDisconnect)

    return () => {
      navigator.usb.removeEventListener('connect', onConnect)
      navigator.usb.removeEventListener('disconnect', onDisconnect)
    }
  }, [device, isSupported])

  // Переподключение к ранее авторизованным устройствам (без диалога)
  async function reconnectPaired() {
    const devices = await navigator.usb.getDevices()
    if (devices.length > 0) {
      const dev = devices[0]
      await connectDevice(dev)
      setDevice(dev)
      setIsConnected(true)
    }
  }

  return { device, isConnected, isSupported, reconnectPaired }
}

Чтение USB-дескриптора

Когда документации нет — читаем дескрипторы устройства напрямую:

function inspectDevice(device: USBDevice) {
  console.log('Vendor ID:', device.vendorId.toString(16))
  console.log('Product ID:', device.productId.toString(16))

  device.configuration?.interfaces.forEach((iface) => {
    console.log(`\nInterface ${iface.interfaceNumber}:`)
    iface.alternates.forEach((alt) => {
      alt.endpoints.forEach((ep) => {
        console.log(`  Endpoint ${ep.endpointNumber}: ${ep.direction} ${ep.type}, packet size: ${ep.packetSize}`)
      })
    })
  })
}

Это позволяет найти нужные endpoint-номера без документации.

Что делаем

Получаем Vendor ID и Product ID устройства, изучаем документацию на протокол или делаем реверс через USB-анализатор. Реализуем подключение, отправку команд и приём данных, обработку отключения, UI статуса. Тестируем на целевых ОС (Windows требует особого внимания с WinUSB-драйверами).

Срок: интеграция с задокументированным USB-устройством — 3–5 дней. Реверс-инжиниринг протокола — 8–14 дней.