USB HID Example – Microsoft C++

by Chris Courson

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);
}
Tagged

Leave a Reply

Your email address will not be published.
*
*