Usb hid component for windows c

Здесь рассмотрено ПО хоста для взаимодействия с устройствами USB HID через одну из программных библиотек. Протокол USB очень сложен, и поэтому обычной практикой работы с ним является использование готовых библиотек подпрограмм (в виде DLL или исходного кода).

Один из вариантов использования интерфейса USB — применение стандартного класса устройств USB HID. Как известно, устройства USB HID очень удобны для подключения пользовательских устройств к компьютеру, так как не требуют написания драйвера, и максимальная скорость обмена с USB HID (64 килобайта/сек) в большинстве случаев оказывается достаточной.

[Краткий обзор библиотек]

Для наглядности библиотеки сведены в таблицу, далее более подробно рассмотрена каждая из библиотек.

Библиотека
Поддерживаемые OS
Автор
Доступность исходного кода
Предпочтительный язык программирования для использования в ПО хоста
Удобство использования по 5-бальной шкале
Ссылка на источник информации
HID.dll Windows Microsoft нет C++ 2 MSDN
AtUsbHid.dll Windows 32-bit Atmel нет C++, Java 5 doc7645.pdf
HID API Windows, Linux, Mac OS X signaleleven, Alan Ott да GCC, C++ 4 hidapi
LibUSB Windows, Linux, FreeBSD, NetBSD, OpenBSD, Darwin, MacOS X libusb.org да GCC, C++ 4 libusb
USB HID Component for Windows C# Windows ?? да C# 4 USB HID C#
JEDI Visual Component Library (JVCL) Windows jvcl.delphi-jedi.org да Delphi7 (Object Pascal) 5 JEDI Visual Component Library
Processing HIDSerialMonitor example Windows, Linux, MacOS X rayshobby.net Да Processing 4 HID-class USB Serial Communication for AVRs using V-USB

Все примеры программ, перечисленные в таблице, Вы можете скачать по ссылке [1].

[HID.dll]

Пример ПО хоста (см. скриншот), которое общается с USB HID через библиотеку HID.dll, имеется в Windows DDK (см., например c:\WINDDK2003\3790.1830\src\wdm\hid\hclient\ для Windows DDK 2003). Сама HID.dll находится в папке %windir%\system32\, и всегда доступна для доступа использующих её программ.

Статью, описывающую пример, см. в [2]. Для того, чтобы скомпилировать пример, Вам нужен Windows DDK любой версии. Писать программу с использованием примера удобнее всего в Visual Studio C++, однако если Вы умеете загружать в память библиотеки DLL и вызывать её функции, то язык и система программирования никакого значения не имеют. Пример ПО хоста не привязан к конкретному USB HID устройству, просто предоставляет интерфейс для получения информации по подключенным устройствам USB HID. Достоинство использования HID.dll в том, что на платформе Windows это наиболее «прямой» метод получения доступа к устройствам USB HID, тогда как остальные библиотеки просто являются обертками над вызовами HID.dll. Недостаток библиотеки — неудобный интерфейс API, отсутствие исходного кода, ограниченное использование — только на операционных системах Windows. Исходный код примера можно взять в Windows DDK или скачать по ссылке [1], см. папку HID.dll\hclient\.

[HID.dll / hidlibrary.h]

Заголовочный файл, написанный неким XANDER, предоставляющий упрощенный доступ к функциям HID.dll. Цитата из комментария к файлу: «Библиотека для работы с Hid. Написана с применением шаблонов. Она с помощью WINAPI функции LoadLibrary подгружает hid.dll, затем GetProcAddress — вычисляет адреса функций внутри hid.dll. Предоставляет удобный и простой интерфейс для работы с HID. Нашел на http://speleoastronomy.org/elektro/atmega/usingusb.html, дописал функцию IsConnectedDevice().«. Заголовочный файл может использоваться совместно Borland CBuilder, Embarcadero RAD studio XE C++ Builder и другими IDE, поддерживающими шаблоны на языке C++. Пример ПО хоста (автор Обрубов Виталий, [obruboff.ru]), работающий с поворотным энкодером, можно скачать по ссылке [1], см. папку HID.dll\usb-volume-control-USB-HID.

[AtUsbHid.dll]

Библиотека достаточно хорошо документирована, имеет простой интерфейс API для использования. Есть примеры GUI-программ, написанных на C++ и Java, а также пример консольной программы.

У этой библиотеки, к сожалению, есть очень большой недостаток — разработчик (компания Atmel) её давно забросил где-то с 2008 года, и исходного кода не предоставил. Есть поддержка только 32-bit версий Windows, 64-bit версии не поддерживаются.

Удобнее всего программировать в среде Visual Studio, однако если Вы умеете загружать в память библиотеки DLL и вызывать её функции, то язык и система программирования никакого значения не имеют. Демонстрационная программа ПО хоста рассчитана на подключение к USB HID, собранному на основе чипа AVR с аппаратным USB и прошитым соответствующим firmware (например, чип AT90USB162, который установлен на макетной плате AVR-USB162 или AVR-USB162MU). Обзор работы с примером можно найти в [3]. Firmware управляет светодиодами и читает состояние кнопок (светодиоды и кнопки подключены к ножкам чипа AT90USB162). С библиотекой AtUsbHid.dll необязательно использовать именно микроконтроллеры Atmel, можно управлять любыми устройствами USB HID. Достоинство AtUsbHid.dll — простота применения. Недостаток — вместе с программой нужно предоставлять библиотеку AtUsbHid.dll, а также ограниченное использование — только на операционных системах Windows. Исходный код примера ПО хоста (и скомпилированные рабочие бинарники) можно найти на сайте Atmel или скачать по ссылке [1], см. папку AtUsbHid.dll\. Код firmware USB HID (исходники и прошивки) можно найти в папке AtUsbHid.dll\firmware\AVR-USB162 и в папке AtUsbHid.dll\firmware\userial (firmware для макетных плат AVR-USB162 и userial соответственно) архива по ссылке [1]. Для указаний по компиляции и использования примеров firmware читайте соответствующие файлы readme.txt в папках AVR-USB162 и userial. Также см. на сайте Atmel апноут AVR328: USB Generic HID Implementation on megaAVR devices, и статью [14].

[HID API]

Кроссплатформенная библиотека с открытым исходным кодом, хорошо документированная. Самый лучший пример использования (ПО хоста и firmware USB HID для микроконтроллера AT90USB162), который мне удалось найти, это проект lightpack см. [5].

Исходный код примера ПО хоста (и скомпилированные рабочие бинарники) можно скачать по ссылке [1], см. папку hidapi-0.5.2 и папку lightpack. Чтобы скомпилировать ПО хоста lightpack, Вам понадобится среда программирования QT, а чтобы скомпилировать firmware USB HID (для чипа AT90USB162, макетная плата AVR-USB162), понадобится пакет WinAVR. Для указаний по компиляции и использования примеров firmware читайте соответствующие файлы readme.txt.

[LibUSB]

Широко известная кроссплатформенная библиотека, используется во многих коммерческих продуктах. Хорошо документирована, исходный код открыт, примеры кода ПО хоста на ней найти довольно легко. На мой взгляд, самые лучшие примеры есть в составе библиотеки V-USB (см. [6]), причем примеры ПО хоста libusb сопряжены с firmware устройства USB HID (на библиотеке V-USB). Т. е. у Вас есть готовые примеры ПО хоста и готовые примеры устройств USB HID, которые обмениваются с этим ПО хоста данными. Устройства USB HID в библиотеке V-USB рассчитаны на обычные чипы AVR, которые не имеют на борту аппаратного контроллера USB (т. е. протокол USB обрабатывается внутри AVR чипа программно), однако можно написать программу, которая будет работать с любым устройством USB. Примеры ПО хоста рассчитаны на GCC (язык C) или Visual Studio (язык C++), однако если Вы знаете, как загружать в память DLL и вызывать её функции, то нет проблем портировать примеры на любую другую среду программирования (см. [7]). Недостаток библиотеки libusb в том, что для ПО хоста, написанной на ней, под Windows нужна установка самой библиотеки, а также нужна привязка к устройству USB HID специального драйвера фильтра (filter driver). Этот драйвер фильтра устанавливается с помощью специальной утилиты, входящей в состав пакета библиотеки libusb.

Исходный код примеров ПО хоста (и скомпилированные рабочие бинарники) можно скачать по ссылке [1], см. папки libusb\examples\hid-custom-rq\commandline, hid-custom-rq\set-led-gui, hid-data\commandline. Имеется также удобная тест-программа (папка examples\usbtool), показывающая параметры всех подключенных к компьютеру устройств USB (для которых также установлен драйвер фильтра). Код firmware USB HID (исходники и прошивки) можно найти в папках hid-custom-rq\firmware-mega, hid-custom-rq\firmware-tiny, hid-data\firmware, hid-mouse\firmware. Примеры firmware удобно запускать на макетных платах AVR-USB-MEGA16 (микроконтроллер ATmega32). Чтобы скомпилировать ПО хоста, Вам необходим компилятор GCC и набор утилит MinGW (см. [8]), или (для примера hid-custom-rq\set-led-gui) Visual Studio C++. Для указаний по компиляции и использования примеров firmware и ПО хоста читайте соответствующие файлы readme.txt, или см. информацию вики LibUSB [9]. Бинарники LibUSB, достаточные для запуска приложений Windows (в том числе и драйвер фильтра filter driver и мастер по его установке) можно скачать по ссылке [11], см файл с названием наподобие libusb-win32-devel-filter-1.2.5.0.exe (цифры версии могут отличаться).

[USB HID Component for Windows C#]

Библиотека с открытым исходным кодом, хорошо документированная (имеется специальный файл подсказки *.CHM), можно использовать только в среде Windows, и удобна только для программ, написанных на C#. Библиотека хороша тем, что к извлечению устройства USB и к моментам передачи данных можно привязать события программы. Библиотеку можно скачать по ссылке [1], см. папку UsbLibraryC#. Для компиляции нужен Visual Studio (хорошо подходит Visual Studio C# 2010 Express, который можно скачать и бесплатно использовать в некоммерческих целях).

См. также описание HID USB Driver / Library for .Net / C# (DLL) [12] для использования в проектах Visual Studio. DLL написана на C#, легко интегрируется в проекты Visual Studio, и имеет простой и понятный программный интерфейс.

[JEDI Visual Component Library (JVCL)]

Библиотека визуальных и невизуальных компонентов для популярной среды разработки Delphi. Пользоваться библиотекой довольно просто, хороший пример использования — конфигуратор устройства USB HID для управления частотой вращения вентиляторов системного блока в зависимости от температуры [15].

Здесь приведен только краткий обзор классов и компонентов JVCL, относящийся к управлению устройствами USB HID.

TJvHidDeviceController предоставляет доступ списку USB контроллеров, отслеживает изменение состояния контроллеров, подключение/отключение устройств.

TJvHidDeviceController:OnDeviceChange — обработчик подключения/отключения устройств.

TJvHidDeviceController:OnEnumerate — получение списка устройств.

TJvHidDevice — предоставляет доступ к конечному USB устройству. В конфигураторе используются только два метода для получения и отправки пакета данных.

TJvHidDevice:GetFeature — получение пакета данных (прием данных от устройства).

TJvHidDevice:SetFeature — отправка пакета данных (передача данных в устройство).

[Processing]

Пример работы с устройством HID (HIDSerialMonitor) предназначен для плат Arduino и USnooBie. В репозитории на GitHub [16] можно найти как готовые скомпилированные бинарники для Linux (32-бит, 64-бит) и для Windows, так и исходный код на языке Processing. Код использует вызовы библиотеки hidapi. Скомпилированные программы и отдельные приложения не требуют никакого дополнительного программного обеспечения.

Программа HIDSerialMonitor нужна как альтернатива Serial Monitor в среде разработки Arduino, это позволяет освободить порт UART микроконтроллера.

Чтобы скомпилировать код самостоятельно, Вам нужно установить Processing (см. сайт processing.org), и скопировать библиотеки (G4P и hiddapi) в папку библиотек Processing. Затем загрузите HIDSerialMonitor.pde в среде разработки Processing и кликните «Run».

[Что нужно, чтобы попробовать]

При таком разнообразии вариантов реализации обмена с устройством USB HID сначала надо определиться с платформой разработки. Например, если Вы пишете на Java, то лучше всего подойдет библиотека Atmel. Если Вы программируете на C#, то лучше выбрать компонент USB HID C#. Больше всего вариантов для выбора у разработчиков, которые программируют на Visual C++.

Затем нужно определиться с выбором устройства USB HID (если еще его у Вас нет). Дешевый и удобный вариант, для которых есть готовые примеры firmware USB HID — это макетные платы AVR-USB162, AVR-USB162MU, AVR-USB-MEGA16 (см. [4]). Для заливки прошивки firmware в эти платы не нужен программатор, firmware заливается в память чипа через USB бутлоадер. Для макетных плат AVR-USB162 и AVR-USB162MU можно взять готовые примеры кода USB HID от Atmel и из библиотеки LUFA (см. [10]), а для макетной платы AVR-USB-MEGA16 — из библиотеки V-USB (см. [6]).

[Ссылки]

1. 130330USBHID.zip — примеры исходного кода, бинарники (.exe) и прошивки firmware, связанные с использованием описанных в статье библиотек.
2. Пример работы с USB HID из Windows DDK.
3. AVR-USB162: где найти рабочие примеры кода firmware и ПО хоста.
4. Макетные платы с интерфейсом USB.
5. Lightpack: система AMBILIGHT на мониторе домашнего компьютера.
6. V-USB, википедия site:ru.wikipedia.org.
7. AVR-USB-MEGA16: управление устройством USB из GCC, Visual Studio CPP, VB6, Python, Delphi.
8. Разработка устройства USB — как начать работу с библиотеками AVR USB (V-USB) и libusb.
9. LibUSB wiki site:libusb.org.
10. LUFA — бесплатная библиотека USB для микроконтроллеров Atmel AVR.
11. libusb-win32 site:sourceforge.net — ссылки на закачку релизов библиотеки libusb.
12. Библиотека HID USB Library для Visual Studio .Net (C#, Visual Basic).
13. LibUsbDotNet site:libusbdotnet.sourceforge.net — порт библиотеки libusb, оформленный в виде классов C# (специально для использования совместно с Microsoft .NET).
14. Работа с Generic HID Class на платформе Windows PC.
15. USB HID регулятор вращения для компьютерных вентиляторов.
16. Rayshobby HIDSerialMonitor Processing host software site:github.com.

usbhid

usnhid is a C++17 Visual Studio 2017 Solution to interrogate a Windows platform
for any attached USB HID devices and to communicate with them.

The VS Solution comprises 2 projects:

usbhid

This is a DLL project and is the main project component that does all the work in
interrogating the system for its attached USB HID devices. It also provides the
means to read and write to them if the devices allow it.

Note: only the public interface is documented.

consoleTest

This is a console test application for the usbhid dll.
It shows how to use the usbhid dll.
Provided in the VS solution is a C source code file RawHid_test1.ino in the
teensyCode filter folder and directory folder of the same name.
This is the code that is run by a Teensy 3.2 miro-controller — see
https://www.pjrc.com/store/teensy32.html.
The consoleTest project and the RawHid_test1.ino consitute a test
harness for the usbhid project.
An example of output is in the text file example-output.txt in the VS solution
folder and visible in the VS Solution Explorer under filter folder example output.

Note: this project is not documented.

Notes

Version of Visual Studio 2017 Professional: 15.9.3
The latest Visual C++ redistributables.

This is an example of Microsoft C++ USB HID connection and control of the xArm 6-DOF robotic arm.

I created this as a little project to fully understand how to make a USB HID connection and communicate with the xArm in C++. I found plenty of examples but many were riddled with bad methods. This example demonstrates proper usage of the functions used to enumerate and communicate with USB HID devices. It simply reads the battery voltage.

output

Begin.

HidGuid = {4D1E55B2-F16F-11CF-88CB-001111000030}
hDevInfoSet Handle: 0070C5B8
Found at dwMemberIdx: 14
byteArrayBuffer (30): HID\VID_0483&PID_5750&REV_0201
DevicePath: \?\hid#vid_0483&pid_5750#7&35f6a2ad&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
WriteHandle: 00000284
Serial number: 497223563535
Manufacturer: MyUSB_HID
Product: LOBOT
Caps.OutputReportByteLength: 65
Caps.InputReportByteLength: 65
Voltage (mv): 7646

End.

test6.cpp

#include <windows.h>
#include <SetupAPI.h>
#include <hidsdi.h>
#include <string>
#include <iostream>

#pragma comment (lib, "Setupapi.lib")
#pragma comment (lib, "Hid.lib")

void PrintLastError();
void GetManufacturerString(HANDLE hHidDeviceObject);
void GetProductString(HANDLE hHidDeviceObject);
void GetBatteryVoltage(HANDLE hHidDeviceObject);
void GetSerialNumberString(HANDLE hHidDeviceObject);

// vendor_id=0x0483, product_id=0x5750
// Compile using Microsoft c++ command line compiler: cl /EHsc test6.cpp



int main(int argc, char *argv[]) {
    printf("Begin.\n\n");

    HDEVINFO hDevInfoSet;
    SP_DEVINFO_DATA devInfoData;
    SP_DEVICE_INTERFACE_DATA devIfcData;
    PSP_DEVICE_INTERFACE_DETAIL_DATA devIfcDetailData;
    HANDLE hHidDeviceObject;

    DWORD dwMemberIdx = 0, dwSize, dwType;
    GUID hidGuid;
    PBYTE byteArrayBuffer;

    std::string _vid = "0483";
    std::string _pid = "5750";
    std::string _serial_number = "497223563535";
    wchar_t wString[127]; // For USB devices, maximum string length of 126 wchar plus terminating NULL character.

    HidD_GetHidGuid(&hidGuid);

    printf("HidGuid = {%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}\n", 
        hidGuid.Data1, hidGuid.Data2, hidGuid.Data3, 
        hidGuid.Data4[0], hidGuid.Data4[1], hidGuid.Data4[2], hidGuid.Data4[3],
        hidGuid.Data4[4], hidGuid.Data4[5], hidGuid.Data4[6], hidGuid.Data4[7]);

    // Retrieve a list of all present USB devices with a device interface.
    hDevInfoSet = SetupDiGetClassDevs(&hidGuid, NULL, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

    printf("hDevInfoSet Handle: %lp\n", hDevInfoSet);

    if (hDevInfoSet != INVALID_HANDLE_VALUE) {

        while (true) {       

            devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

            // Get SP_DEVINFO_DATA for this member.
            if (!SetupDiEnumDeviceInfo(hDevInfoSet, dwMemberIdx, &devInfoData)) {
                break;
            }

            // Get required size for device property
            SetupDiGetDeviceRegistryProperty(hDevInfoSet, &devInfoData, SPDRP_HARDWAREID, &dwType, NULL, 0, &dwSize);

            // Allocate required memory for byteArrayBuffer to hold device property.
            byteArrayBuffer = (PBYTE) malloc(dwSize * sizeof(BYTE));

            // Get SPDRP_HARDWAREID device property
            if (SetupDiGetDeviceRegistryProperty(hDevInfoSet, &devInfoData, SPDRP_HARDWAREID, &dwType, byteArrayBuffer, dwSize, NULL)) {
                
                // VID and PID
                std::string vid = "VID_" + _vid;
                std::string pid = "PID_" + _pid;
                
                // Test for VID/PID
                if (strstr((char *)byteArrayBuffer, (char *)&pid) && strstr((char *)byteArrayBuffer, (char *)&vid)) {
                    printf("Found at dwMemberIdx: %i\n", dwMemberIdx);
                    printf("byteArrayBuffer (%i): %s\n", strlen((char *)byteArrayBuffer), byteArrayBuffer);

                    devIfcData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
                    SetupDiEnumDeviceInterfaces(hDevInfoSet, NULL, &hidGuid, dwMemberIdx, &devIfcData);

                    // Get required size for devIfcDetailData.
                    SetupDiGetDeviceInterfaceDetail(hDevInfoSet, &devIfcData, NULL, 0, &dwSize, NULL);

                    // Allocate required memory for devIfcDetailData.
                    devIfcDetailData = (PSP_INTERFACE_DEVICE_DETAIL_DATA)malloc(dwSize);
                    devIfcDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

                    // Get devIfcDetailData
                    SetupDiGetDeviceInterfaceDetail(hDevInfoSet, &devIfcData, devIfcDetailData, dwSize, &dwSize, NULL);
                    printf("DevicePath: %s\n", devIfcDetailData->DevicePath);

                    // Get Writehandle of device
                    hHidDeviceObject = CreateFile((devIfcDetailData->DevicePath), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
                    printf("WriteHandle: %lp\n", hHidDeviceObject);

                    GetSerialNumberString(hHidDeviceObject);
                    GetManufacturerString(hHidDeviceObject);
                    GetProductString(hHidDeviceObject);
                    GetBatteryVoltage(hHidDeviceObject);

                    CloseHandle(hHidDeviceObject);

                    // Release devIfcDetailData memory
                    free(devIfcDetailData);
                }                
            }

            // Release byteArrayBuffer memory
            free(byteArrayBuffer);

            dwMemberIdx++;
        }

    } else {
        printf("devInfo == INVALID_HANDLE_VALUE\n");
    }

    SetupDiDestroyDeviceInfoList(hDevInfoSet);

    printf("\nEnd.\n");
}

void PrintLastError() {
    
    DWORD dwMessageId = GetLastError();
    LPSTR lpBuffer = nullptr;

    size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL, dwMessageId, 0, (LPSTR)&lpBuffer, 0, NULL);    

    printf("LastError: %s\n", lpBuffer);

    LocalFree(lpBuffer);
}

void GetBatteryVoltage(HANDLE hHidDeviceObject) {

    PHIDP_PREPARSED_DATA PreparsedData;
    HIDP_CAPS Caps;
    DWORD dwRead;
    DWORD dwWritten;

    HidD_GetPreparsedData(hHidDeviceObject, &PreparsedData);

    HidP_GetCaps(PreparsedData, &Caps);

    printf("Caps.OutputReportByteLength: %i\n", Caps.OutputReportByteLength);
    printf("Caps.InputReportByteLength: %i\n", Caps.InputReportByteLength);

    HidD_FreePreparsedData(PreparsedData);

    PBYTE OutputReport = (PBYTE) malloc(Caps.OutputReportByteLength);
    OutputReport[0] = 0x00; // Record #
    OutputReport[1] = 0x55; // Header
    OutputReport[2] = 0x55; // Header
    OutputReport[3] = 0x02; // Len
    OutputReport[4] = 0x0F; // Get battery voltage

    WriteFile(hHidDeviceObject, OutputReport, Caps.OutputReportByteLength, &dwWritten, 0);

    Sleep(500);

    PBYTE InputReport = (PBYTE) malloc(Caps.InputReportByteLength);

    ReadFile(hHidDeviceObject, InputReport, Caps.InputReportByteLength, &dwRead, 0);

    int voltage = InputReport[6] * 256 + InputReport[5];
    printf("Voltage (mv): %i\n", voltage);

    free(InputReport);
    free(OutputReport);
}

void GetManufacturerString(HANDLE hHidDeviceObject) {

    PWCHAR Buffer = (PWCHAR) malloc(127);
    if (HidD_GetManufacturerString(hHidDeviceObject, Buffer, 127)) {
        printf("Manufacturer: %ws\n", Buffer);
    } else {
        PrintLastError();
    }
    free(Buffer);
}

void GetProductString(HANDLE hHidDeviceObject) {

    PWCHAR Buffer = (PWCHAR) malloc(127);
    if(HidD_GetProductString(hHidDeviceObject, Buffer, 127)) {
        printf("Product: %ws\n", Buffer);
    } else {
        PrintLastError();
    }
    free(Buffer);
}

void GetSerialNumberString(HANDLE hHidDeviceObject) {

    PWCHAR Buffer = (PWCHAR) malloc(127);
    if(HidD_GetSerialNumberString(hHidDeviceObject, Buffer, 127)) {
        printf("Serial number: %ws\n", Buffer);
    } else {
        PrintLastError();
    }
    free(Buffer);
}

USB Human Interface Devices (HID) play a crucial role in enabling communication between hardware peripherals and computers. From keyboards and mice to specialized hardware like gaming controllers or industrial sensors, HID devices rely on descriptors to define their behavior. For developers working in C, mastering the creation and customization of HID descriptors is essential to unlocking the full potential of these devices.

This article dives into the intricacies of handling advanced USB HID devices using custom descriptors. Whether you’re developing a custom peripheral for specialized hardware or looking to refine your understanding of USB communication, this guide provides practical insights, tools, and examples to help you achieve your goals efficiently.

TOC

Overview of USB HID Protocol

The USB Human Interface Device (HID) protocol is a standardized communication mechanism designed for devices like keyboards, mice, joysticks, and other user input hardware. It ensures seamless interaction between devices and host systems without requiring device-specific drivers.

Why the HID Protocol is Important

The HID protocol simplifies device integration by using predefined descriptors to communicate device capabilities and requirements. This standardization allows plug-and-play functionality, ensuring that most operating systems can immediately recognize and utilize HID devices.

Key Components of the USB HID Protocol

  1. HID Class Descriptor: Defines the characteristics of the device, such as its type and capabilities.
  2. HID Report Descriptor: Specifies the format of data packets exchanged between the host and the device.
  3. Data Reports: The actual data exchanged, such as keypresses or sensor readings.

Benefits of Using USB HID for Specialized Hardware

  • Cross-Platform Compatibility: Supported by most operating systems.
  • Ease of Implementation: No need for complex drivers or additional software.
  • Flexibility: Allows customization through user-defined descriptors.

Understanding these fundamental aspects of the USB HID protocol is the first step in creating efficient and versatile HID devices in C.

Understanding HID Report Descriptors

HID Report Descriptors are at the core of USB HID devices, defining how data is exchanged between the device and the host. They describe the structure and format of data reports, enabling seamless communication without requiring custom drivers.

What Is an HID Report Descriptor?

An HID Report Descriptor is a binary data structure that specifies:

  • Data Layout: Defines the size and position of each data field.
  • Report Type: Identifies input, output, or feature reports.
  • Usage: Indicates the function of each field, such as buttons, axes, or custom values.

Key Elements of an HID Report Descriptor

  1. Global Items: Apply settings like report size and usage page to multiple fields.
  • Example: Usage Page (Generic Desktop) specifies the type of device, such as a joystick or gamepad.
  1. Local Items: Define attributes for individual fields, like usage or logical range.
  • Example: Usage (X) identifies an axis of movement.
  1. Main Items: Indicate the purpose of a field, such as input or output.
  • Example: Input (Data, Variable, Absolute) describes a joystick axis.

Writing a Custom HID Report Descriptor

Here is a sample HID Report Descriptor for a custom game controller:

const uint8_t customHIDReportDescriptor[] = {
    0x05, 0x01,  // Usage Page (Generic Desktop)
    0x09, 0x04,  // Usage (Joystick)
    0xA1, 0x01,  // Collection (Application)
    0x05, 0x09,  // Usage Page (Button)
    0x19, 0x01,  // Usage Minimum (Button 1)
    0x29, 0x08,  // Usage Maximum (Button 8)
    0x15, 0x00,  // Logical Minimum (0)
    0x25, 0x01,  // Logical Maximum (1)
    0x95, 0x08,  // Report Count (8)
    0x75, 0x01,  // Report Size (1)
    0x81, 0x02,  // Input (Data, Variable, Absolute)
    0xC0         // End Collection
};

Practical Considerations

  • Validation: Use USB analyzers to verify your descriptor.
  • Compliance: Ensure compatibility with the HID protocol and host OS expectations.
  • Optimization: Minimize unused fields to reduce report size and improve performance.

Mastering HID Report Descriptors enables the creation of highly customized devices that meet specific functional requirements.

Setting Up the Development Environment

Developing advanced USB HID devices in C requires a properly configured development environment. This ensures smooth coding, compilation, and debugging of HID-related programs.

Essential Tools and Software

To start, you’ll need the following:

  1. C Compiler: A compiler like GCC or Clang to build your C programs.
  2. USB Library: Libraries like libusb or hidapi for handling USB communication.
  3. Development Board (Optional): If you’re working with hardware, a microcontroller such as an Arduino, STM32, or Raspberry Pi is often used.
  4. USB Sniffer/Analyzer: Tools like Wireshark or dedicated USB analyzers for debugging data traffic.

Installing Libraries for USB HID Development

  1. Install libusb:
  • On Linux:
    bash sudo apt-get install libusb-1.0-0-dev
  • On Windows: Use package managers like vcpkg or download from the official website.
  1. Install hidapi:
  • Clone the repository:
    bash git clone https://github.com/libusb/hidapi.git cd hidapi cmake . make sudo make install

Configuring the Development Environment

  1. Set Up IDE: Use an IDE like Visual Studio Code, Eclipse, or CLion for project management and debugging.
  2. Link USB Libraries: Ensure your project links to the required USB libraries.
  • Example GCC command:
    bash gcc -o myhiddevice myhiddevice.c -lusb-1.0
  1. Create a Makefile: Automate the build process with a Makefile.
    Example Makefile:
   all:
       gcc -o myhiddevice myhiddevice.c -lusb-1.0
   clean:
       rm -f myhiddevice

Testing the Environment

Run a basic USB detection program to verify the setup:

#include <libusb-1.0/libusb.h>
#include <stdio.h>

int main() {
    libusb_context *ctx;
    libusb_init(&ctx);
    printf("LibUSB initialized successfully!\n");
    libusb_exit(ctx);
    return 0;
}

Compile and run the program. If you see a success message, your environment is ready.

Key Tips for a Smooth Setup

  • Permissions: Ensure you have the necessary permissions to access USB devices, especially on Linux.
  • Documentation: Refer to library documentation for advanced features and troubleshooting.
  • Updates: Keep your tools and libraries updated for the latest compatibility and features.

A well-prepared development environment is the foundation for successful USB HID device programming.

Writing and Configuring HID Descriptors

Custom HID descriptors are crucial for defining how a USB HID device communicates with the host. This step involves writing descriptors tailored to your hardware’s functionality and ensuring proper configuration for seamless interaction.

Understanding HID Descriptor Structure

The HID Descriptor includes several key components:

  1. Descriptor Header: Specifies the length and type of the descriptor.
  2. Report Descriptor Pointer: Points to the HID Report Descriptor, defining the data format.
  3. HID Protocol: Indicates whether the device uses boot protocol or report protocol.

Example structure of an HID Descriptor:

typedef struct {
    uint8_t length;       // Length of the descriptor
    uint8_t descriptorType; // HID descriptor type
    uint16_t bcdHID;      // HID Class Specification version
    uint8_t countryCode;  // Country code
    uint8_t numDescriptors; // Number of class descriptors
    uint8_t reportDescriptorType; // Type of the next descriptor
    uint16_t reportDescriptorLength; // Length of the report descriptor
} HIDDescriptor;

Steps to Write a Custom HID Descriptor

  1. Identify the Device’s Functionality:
  • Determine what data the device will send/receive (e.g., joystick axis, button states).
  1. Create the Report Descriptor:
  • Define the structure of data reports as shown in the previous example.
  1. Configure the HID Descriptor:
  • Populate the fields based on your device’s requirements.
    Example HID Descriptor initialization:
   HIDDescriptor myHIDDescriptor = {
       .length = 9,
       .descriptorType = 0x21,
       .bcdHID = 0x0111, // HID 1.11
       .countryCode = 0,
       .numDescriptors = 1,
       .reportDescriptorType = 0x22, // Report Descriptor
       .reportDescriptorLength = sizeof(customHIDReportDescriptor)
   };

Integrating Descriptors into Your Firmware

  • Embedded Devices: Use libraries like STM32 HAL or Arduino USB libraries to send descriptors during enumeration.
  • Host-Side Testing: Use tools like lsusb on Linux to inspect the descriptors sent by your device:

Validating HID Descriptors

  1. USB Validation Tools: Use USB protocol analyzers to validate the descriptor structure.
  2. OS Compatibility Testing: Test the device on multiple operating systems to ensure compatibility.
  3. HID Descriptor Tester: Tools like USB Descriptor Verification Tool (available online) can catch common issues.

Best Practices for HID Descriptor Configuration

  • Keep It Simple: Avoid unnecessary fields to minimize descriptor size.
  • Follow the HID Specification: Adhere to the USB HID standard to ensure broad compatibility.
  • Iterate and Test: Incrementally build and test your descriptor to catch issues early.

Properly written and configured HID descriptors allow your device to communicate efficiently and adapt to diverse use cases, paving the way for robust USB HID applications.

USB HID Class Drivers and Their Functions

HID class drivers serve as the bridge between USB HID devices and the operating system, enabling seamless communication and device functionality. Understanding their role and how to integrate them into your projects is essential for successful USB HID development.

What Are HID Class Drivers?

HID class drivers are system-level software components that handle the communication protocol between the host and HID devices. They abstract the complexities of USB communication, allowing developers to focus on higher-level functionality.

Key Functions of HID Class Drivers

  1. Device Enumeration:
  • When a device is connected, the driver reads its descriptors to identify capabilities.
  • The host uses this information to set up communication channels.
  1. Data Parsing and Transfer:
  • The driver processes input and output reports based on the Report Descriptor.
  • Example: Translating a joystick’s axis data into actionable events for a game.
  1. Protocol Handling:
  • Supports both Boot Protocol (used by keyboards and mice for basic functionality) and Report Protocol (for custom data formats).
  1. Error Management:
  • Handles USB-specific errors like timeouts or transmission issues, ensuring reliability.

Integrating HID Class Drivers in Development

  1. Host-Side Integration:
  • On most modern operating systems, HID class drivers are built-in.
  • Examples:
    • Windows: hid.dll
    • Linux: usbhid kernel module
  1. Device-Side Integration:
  • If you’re developing firmware for a USB HID device, ensure it adheres to the HID specification so that class drivers can automatically manage it.

Example initialization using the STM32 HAL library:

USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);
USBD_RegisterClass(&hUsbDeviceFS, USBD_HID_CLASS);
USBD_Start(&hUsbDeviceFS);

Testing HID Class Driver Functionality

  1. Basic Detection:
  • On Windows, check the device in the Device Manager under “Human Interface Devices.”
  • On Linux, use dmesg | grep HID to view connected devices.
  1. Data Communication:
  • Use tools like hidtest or custom host-side programs to send and receive reports.

Example using hidapi in C to send data:

hid_device *device = hid_open(vendor_id, product_id, NULL);
unsigned char data[4] = {0x01, 0x02, 0x03, 0x04};
hid_write(device, data, sizeof(data));
hid_close(device);

Advantages of Using HID Class Drivers

  • Plug-and-Play: Devices automatically work without additional driver installation.
  • Cross-Platform Support: Supported on all major operating systems.
  • Customizability: Report Descriptor flexibility allows for tailored device functionality.

Best Practices for Working with HID Class Drivers

  • Adhere to Standards: Ensure your descriptors are compliant with HID specifications for compatibility.
  • Test Thoroughly: Verify functionality across multiple platforms and scenarios.
  • Optimize Performance: Minimize report size and frequency to reduce latency and improve efficiency.

HID class drivers simplify the integration and functionality of USB HID devices, making them an indispensable part of developing reliable and efficient applications.

Sending and Receiving Data with HID Devices

Effective communication between a host and USB HID devices is crucial for any application. This involves sending and receiving data using HID reports, as defined in the device’s descriptors. In this section, we explore how to implement data communication in C for USB HID devices.

HID Communication Workflow

The communication process typically follows these steps:

  1. Enumeration: The device provides its descriptors, allowing the host to understand its capabilities.
  2. Data Transfer: The host sends output reports to the device, and the device sends input reports to the host.

Using Libraries for HID Communication

Libraries like hidapi and libusb simplify data exchange between HID devices and host systems.

Sending Data to the Device

Use output reports to send data from the host to the HID device. For instance, configuring an LED on a custom controller:

#include <hidapi/hidapi.h>
#include <stdio.h>

int main() {
    hid_device *device = hid_open(0x1234, 0x5678, NULL); // Replace with your vendor and product IDs
    if (!device) {
        printf("Failed to open device\n");
        return 1;
    }

    unsigned char report[2] = {0x01, 0xFF}; // Report ID and data (turn LED on)
    hid_write(device, report, sizeof(report));

    hid_close(device);
    return 0;
}

Receiving Data from the Device

Use input reports to read data from the HID device, such as button states or sensor values:

unsigned char buffer[16];
int bytes_read = hid_read(device, buffer, sizeof(buffer));
if (bytes_read > 0) {
    printf("Data received: ");
    for (int i = 0; i < bytes_read; i++) {
        printf("%02x ", buffer[i]);
    }
    printf("\n");
}

Handling Feature Reports

Feature reports provide bi-directional communication for device configuration. These are less common but useful for settings or special commands.

Example:

unsigned char feature_report[16] = {0x02}; // Feature report ID
hid_get_feature_report(device, feature_report, sizeof(feature_report));
printf("Feature report received: %02x\n", feature_report[1]);

Debugging and Testing Communication

  1. Verify Vendor and Product IDs: Ensure the correct IDs are used when opening the device.
  2. Test Reports with Tools: Use software like USBlyzer (Windows) or Wireshark with USB capture to analyze HID reports.
  3. Validate Data Formats: Ensure the data sent matches the format specified in the Report Descriptor.

Practical Considerations

  • Report IDs: Always include the report ID in the first byte of the data array.
  • Buffer Sizes: Match the buffer size with the report size defined in the HID descriptor.
  • Polling Rates: Be mindful of how frequently the host polls the device to avoid performance bottlenecks.

Real-World Application Example

A gaming controller sends button and joystick data to a PC. The input report format is defined as:

  • Byte 0: Report ID (0x01)
  • Byte 1: Button states (bitfield for 8 buttons)
  • Byte 2: X-axis position
  • Byte 3: Y-axis position

The corresponding read code in C:

unsigned char report[4];
int res = hid_read(device, report, sizeof(report));
if (res > 0) {
    printf("Buttons: %02x, X: %d, Y: %d\n", report[1], report[2], report[3]);
}

Conclusion

By mastering data transfer techniques in C, developers can enable robust communication with USB HID devices. Using libraries like hidapi simplifies this process while maintaining flexibility and control. Regular testing and adherence to descriptor specifications ensure smooth functionality and user satisfaction.

Debugging USB HID Communication Issues

Developing USB HID devices can present challenges, especially when communication doesn’t work as expected. Effective debugging techniques are essential to identify and resolve issues. This section outlines strategies to troubleshoot common USB HID communication problems.

Common Issues in USB HID Communication

  1. Descriptor Errors: Incorrect or incomplete descriptors can prevent proper enumeration.
  2. Data Transfer Failures: Mismatched report formats or buffer sizes may cause read/write errors.
  3. Permission Problems: Insufficient permissions on the host system can block communication.
  4. Hardware Malfunctions: Faulty USB cables, ports, or device hardware may result in connectivity issues.

Tools for Debugging

  1. USB Analyzers:
  • Tools like Wireshark with USB capture or dedicated USB analyzers can monitor USB traffic and identify errors.
  1. USB Descriptor Viewers:
  • Tools such as usbview (Windows) or lsusb -v (Linux) help verify descriptor content.
  1. Logging and Debugging Libraries:
  • Use debugging features in libraries like libusb or hidapi to log communication details.

Debugging Steps

1. Validate Device Enumeration

Check whether the device is recognized by the host.

  • On Linux:
  • On Windows: Use Device Manager to check for the device under “Human Interface Devices.”

If the device doesn’t enumerate, verify your HID descriptors for correctness.

2. Verify Descriptor Configuration

Use a descriptor viewer or analyzer to inspect the HID descriptor. Ensure the Report Descriptor matches the intended functionality.

Example check:

lsusb -v -d <vendor_id>:<product_id>

3. Test Data Transfers

Use a sample program to test reading and writing data:

unsigned char buffer[16];
int bytes_written = hid_write(device, buffer, sizeof(buffer));
if (bytes_written < 0) {
    printf("Write error: %s\n", hid_error(device));
}

If errors occur, ensure the data format matches the Report Descriptor.

4. Analyze USB Traffic

Capture USB traffic to debug communication issues:

  • Wireshark: Use the USBPcap plugin to analyze USB packets. Look for malformed reports or failed transactions.
  • USBlyzer (Windows): Provides detailed insights into HID communication.

5. Check Permissions and Drivers

  • On Linux, ensure you have proper permissions to access USB devices.
    Example: Add a udev rule for your device:
  SUBSYSTEM=="usb", ATTR{idVendor}=="1234", ATTR{idProduct}=="5678", MODE="0666"
  • On Windows, confirm that hid.dll is functioning correctly.

6. Debug Hardware Issues

  • Test the device with a different USB cable or port.
  • Use a known-good HID device to verify the host setup.

Real-World Example

Problem: A custom joystick device is recognized but fails to send data.

  • Analysis: The Report Descriptor defines a 3-byte input report, but the host is polling a 4-byte buffer.
  • Solution: Update the Report Descriptor to include the correct report size or adjust the host application to match the descriptor.

Best Practices for Debugging USB HID

  • Incremental Testing: Test each component (descriptors, hardware, software) independently.
  • Documentation: Keep detailed notes on descriptor configurations and data formats.
  • Regular Validation: Use USB analyzers and logging to catch issues early in development.

Conclusion

Debugging USB HID communication requires a systematic approach, combining tools and careful analysis. By validating descriptors, monitoring data traffic, and addressing hardware and permission issues, developers can ensure reliable device functionality and a smoother development process.

Practical Example: Custom Gaming Controller

To demonstrate USB HID development in C, let’s create a custom gaming controller that interacts with a PC. This project involves defining HID descriptors, configuring hardware, and implementing data communication for buttons and joystick inputs.

Project Overview

The custom gaming controller will have:

  1. 8 Buttons: Represented as a single byte using a bitfield.
  2. 2 Joystick Axes: Represented as signed 8-bit values for X and Y positions.
  3. USB HID Communication: Data will be sent to the host as an input report.

Step 1: Define the HID Report Descriptor

The Report Descriptor specifies how data is formatted and communicated:

const uint8_t customHIDReportDescriptor[] = {
    0x05, 0x01,  // Usage Page (Generic Desktop)
    0x09, 0x04,  // Usage (Joystick)
    0xA1, 0x01,  // Collection (Application)
    0x05, 0x09,  // Usage Page (Button)
    0x19, 0x01,  // Usage Minimum (Button 1)
    0x29, 0x08,  // Usage Maximum (Button 8)
    0x15, 0x00,  // Logical Minimum (0)
    0x25, 0x01,  // Logical Maximum (1)
    0x95, 0x08,  // Report Count (8)
    0x75, 0x01,  // Report Size (1)
    0x81, 0x02,  // Input (Data, Variable, Absolute)
    0x05, 0x01,  // Usage Page (Generic Desktop)
    0x09, 0x01,  // Usage (Pointer)
    0xA1, 0x00,  // Collection (Physical)
    0x09, 0x30,  // Usage (X)
    0x09, 0x31,  // Usage (Y)
    0x15, 0x81,  // Logical Minimum (-127)
    0x25, 0x7F,  // Logical Maximum (127)
    0x75, 0x08,  // Report Size (8)
    0x95, 0x02,  // Report Count (2)
    0x81, 0x02,  // Input (Data, Variable, Absolute)
    0xC0,        // End Collection
    0xC0         // End Collection
};

Step 2: Configure the Firmware

Load the HID descriptor into your device’s firmware and set up data transfer. Here’s an example using an STM32 microcontroller:

#include "usbd_hid.h"

USBD_HID_HandleTypeDef hHID;

void HID_Init(void) {
    USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);
    USBD_RegisterClass(&hUsbDeviceFS, USBD_HID_CLASS);
    USBD_Start(&hUsbDeviceFS);
}

void HID_SendReport(uint8_t *report, uint16_t len) {
    USBD_HID_SendReport(&hUsbDeviceFS, report, len);
}

Step 3: Build the Input Report

The input report structure matches the Report Descriptor:

  • Byte 0: Button states (bitfield for 8 buttons).
  • Byte 1: X-axis position (-127 to 127).
  • Byte 2: Y-axis position (-127 to 127).

Example:

uint8_t inputReport[3];
inputReport[0] = 0b00001111; // Buttons 1-4 pressed
inputReport[1] = 50;         // X-axis at +50
inputReport[2] = -50;        // Y-axis at -50

HID_SendReport(inputReport, sizeof(inputReport));

Step 4: Test the Gaming Controller

  1. Connect the Device: Plug the microcontroller into your PC via USB.
  2. Verify Enumeration: Use lsusb or Device Manager to confirm recognition.
  3. Test Input Reports: Use a tool like Joystick Tester or Gamepad Tester to visualize button and joystick inputs.

Real-World Enhancements

  • Add More Axes or Buttons: Expand the Report Descriptor to include additional inputs.
  • Introduce Vibration Feedback: Implement output reports for force feedback.
  • Build a Custom Driver: Use HID APIs to enhance functionality for specific applications.

Conclusion

This example showcases the practical application of USB HID development, from descriptor design to firmware implementation and testing. By following these steps, you can create a fully functional custom gaming controller or adapt the principles for other specialized HID devices.

Conclusion

Developing USB HID devices in C requires a strong understanding of descriptors, drivers, and data communication. This article has guided you through the core concepts, from configuring HID descriptors to implementing real-world examples like a custom gaming controller.

By mastering the USB HID protocol, you can design devices that seamlessly integrate with host systems, ensuring reliability and flexibility. With tools like hidapi and techniques for debugging, you can overcome challenges and create innovative solutions for specialized hardware. Remember, careful planning, adherence to standards, and rigorous testing are key to successful HID device development.

A C# project to handle multiple generic HID USB devices

Description

Since .net doesn’t include ready-made classes to talk to generic HID USB devices, wrappers around the windows API are needed. If you’re interested in windows store applications, the
relevant WS API has included such functionality, but limits targets to windows store apps (obviously) and at least windows 8.1/10. This project can be used with windows 7 — maybe
even Vista and XP but who cares about them really 😃

Inspiration and original code came from the work of Szymon Roslowski, published in a codeplex article with the
very permissive Code Project Open License. Szymon’s code is indeed one of the cleanest out there but is unmaintained for several years
and lucks support for talking to multiple HID devices sharing the same VID/PID (Vendor/Product ID) since only one device (the first discovered) for a given VID/PID can be
instantiated. Also no notification for newly inserted devices is provided, one has to use the cumbersome windows API to get notified or poll continuously.

Instantiating only one device is a problem when using several of the same VID/PID pair devices, usually because you can’t afford the (upwards of $5000) payment to USB.org to get your
own VID and have to resort to sharing the public provided HID VID/PID pair offered by Objective Development.

Since on the job, a major code cleanup is underway to remove unnecessary checks, simplify loops, reduce cyclomatic complexity and create happy paths instead of else‘s and nested
if‘s. Cleaned up code is mostly self-documenting so most comments are removed, code style is enforced and Debug.WriteLine() statements are kept to the minimum.

USB discovery and device instantiation in Windows

USB is pretty straight forward for the end user but quite complicated from a hardware/software implementation perspective. I will not attempt to go in depth here, there’s excellent
information on the subject in other places. The explanation here covers the basics needed to understand the process of finding and using a device in windows.

Each USB device that serves one purpose has to be identified by a unique VID/PID, i.e. a printer or a joystick. This makes uniquely paired VID/PID devices easy to detect and work
with. If several devices of the same VID/PID are used then they have to have a unique SERIAL number each (i.e. two or more of the same model of printers). Differentiating the
Manufacturer/Product description strings to include a unique identifier will not suffice as these strings are read after identifying a device by VID/PID/SERIAL. The uniqueness
of the VID/PID/SERIAL is not formally enforced in a hard way (i.e. «duplicate» devices being somehow rejected from the bus when connected), so you can get away with it and some other
means to identify duplicate devices is needed. Finally when no SERIAL is used, 0 is assumed, so technically the serial is used all the times.

To solve this problem, windows will generate a unique device instance path string for each USB device found. It also takes the REV (revision) number into account and for those that
are found to share the same VID/PID(/REV)/SERIAL a random, unique string, is inserted in each device’s instance path. For HID devices it would look like
\\\\?\\hid#vid_16c0&pid_27d9#7&62250e9&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} in code and HID\VID_16C0&PID_27D9\3&62250E9&0&0000 in device manager.

This string contains a «random» part (in this example above 62250E9) that is probably derived from a hash of the USB hub/port number the device is connected on, because it remains
the same when you reconnect the device in the same port, but changes when you connect it in a different port. Thus you can’t expect to persistently identify devices between ports,
computers or maybe even restarts with it. It should be only used to differentiate each device at runtime.

Using this string to instantiate devices allows them to share a VID/PID(/SERIAL). To discover a device based on i.e. its product description string, the device needs to be opened and
the string descriptor read. If the device matches we can use the device instance path to further identify it in the future. Fortunately this two-step process is performed internally.
To match a device use an applicable Matcher class to look for the device(s) you’re after. Matcher classes can be extended from the ones already provided, or created from scratch, by
implementing the IUsbDeviceMatchable interface.

How to use

Use a Matcher class (one that implements the IUsbDeviceMatchable interface) on FindHidDevices() to get a List of KeyValuePairs that contain the device instance path as
key and the descriptor strings as value for each matching device. Then use any of the returned device instance path strings to instantiate one or more UsbHidDevice classes that
access the actual devices. Currently two matcher classes are provided:

VidPidMatcher provides the original matching behavior against a VID/PID pair. Use like:

var devices = FindHidDevices(new VidPidMatcher(0xDEAD, 0xBEEF));

SerialStringMatcher matches against (the start of) the USB descriptor serial string and the public VID/PID pair provided by Objective Development or any given VID/PID pair.

var devices = FindHidDevices(new SerialStringMatcher("mydevice.com:"));
//when following the OD guidelines or when using your own VID/PID as: 
//FindHidDevices(new SerialStringMatcher("SERIAL_TO_MATCH", 0xDEAD, 0xBEEF))

You can parse the returned list with your own logic. For this example, assuming at least one device is returned from FindHidDevices(), then simply instantiate it like:

var myHidDevice = new UsbHidDevice(devices[0].Key); //devices[0].Key contains the instance path string of the first device returned

You can then immediately use the device:

myHidDevice.DataReceived += MyHidDevice_DataReceived; //Subscribe your own handler function for the DataReceived event
myHidDevice.SendCommandMessage(0x10); //Send a command message with payload (0x10)

NOTE: The events for (new) device connection/disconnection are not yet available in this version. You can instead poll for new devices. Adding this requires a major refactor.

Disclaimer

I mostly adapted this to serve my own needs. I don’t attempt to cover everyone’s use case. If something doesn’t work as expected please
file an issue but since I work on this on my spare time, no guaranties on fixing it.

License

Code Project Open License

Product

Compatible and additional computed target framework versions.

.NET

net5.0 was computed. 

net5.0-windows was computed. 

net6.0 was computed. 

net6.0-android was computed. 

net6.0-ios was computed. 

net6.0-maccatalyst was computed. 

net6.0-macos was computed. 

net6.0-tvos was computed. 

net6.0-windows was computed. 

net7.0 was computed. 

net7.0-android was computed. 

net7.0-ios was computed. 

net7.0-maccatalyst was computed. 

net7.0-macos was computed. 

net7.0-tvos was computed. 

net7.0-windows was computed. 

net8.0 was computed. 

net8.0-android was computed. 

net8.0-browser was computed. 

net8.0-ios was computed. 

net8.0-maccatalyst was computed. 

net8.0-macos was computed. 

net8.0-tvos was computed. 

net8.0-windows was computed. 

net9.0 was computed. 

net9.0-android was computed. 

net9.0-browser was computed. 

net9.0-ios was computed. 

net9.0-maccatalyst was computed. 

net9.0-macos was computed. 

net9.0-tvos was computed. 

net9.0-windows was computed. 

.NET Core

netcoreapp2.0 was computed. 

netcoreapp2.1 was computed. 

netcoreapp2.2 was computed. 

netcoreapp3.0 was computed. 

netcoreapp3.1 was computed. 

.NET Standard

netstandard2.0 is compatible. 

netstandard2.1 was computed. 

.NET Framework

net461 was computed. 

net462 was computed. 

net463 was computed. 

net47 was computed. 

net471 was computed. 

net472 was computed. 

net48 was computed. 

net481 was computed. 

MonoAndroid

monoandroid was computed. 

MonoMac

monomac was computed. 

MonoTouch

monotouch was computed. 

Tizen

tizen40 was computed. 

tizen60 was computed. 

Xamarin.iOS

xamarinios was computed. 

Xamarin.Mac

xamarinmac was computed. 

Xamarin.TVOS

xamarintvos was computed. 

Xamarin.WatchOS

xamarinwatchos was computed. 

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Как обнулить систему windows 10
  • Пропал просмотрщик фото windows 10
  • Ad1988a драйвер windows 10
  • Windows 7 видит не все компьютеры рабочей группы
  • Thc hydra windows инструкция