This work describes a HID C++ class, planned to be used in electronics / robotics. HID (Human Interface Devices) is very complex and even the simplest thing will be difficult to implement.
This is the reason I have developed the
CUsbHidIO C++ class. This work makes it much more simple for a developer in electronics or robotics, without much knowledge about software development, the using of any HID gamepad or joystick available in market (almost 100% USB gamepads) like a computer interface for either analogical or digital input signals. It is also possible, thanks to HID Led
UsagePage, to use them as digital output control signals.
This work is an extract of the project I named: AINECC ROBOTDOG, and you can get further information on my personal web site, including how to built the electronic board that emulates a gamepad device with use of 10 analog input signals, 1 digital input signal and 16 output signals to robot control, but you don’t need to use this board, just get a gamepad, open it, connect its potentiometers to your robot sensors and its buttons to your robot detectors and get to work.
To Take into Account
There is something that you have to take into account:
- Some Joysticks or gamepads only send its analogical values or button states, just after something has changed. If nothing changes (no button pressed or axis moved), then no report (no values) is send. This restriction must be considered especially if you have planned to work in robotics and you need readings just after switching on the robot.
CUsbHidIO class, makes heavy internal use of “handles” and “memory allocation”, so try do not run pointers. It is better using
- Be careful when using this code because a lot of parameters were discarded in order to get the functionality as much simple as possible.
Using the Code
As I referred before, the
CUsbHidIO class was planned to be used in robotics. The way to use it is so simple. There are only 2 or 3 steps that you will need to obtain readings in either button state or analogical values. It is also possible to switch on any HID device LED, so that you can control robot drivers.
The main code has 3(4) C++ files (xx.cpp):
- RobotDogD1.cpp: This is the main application source file that contains the application. There is nothing inside but the command to launch the main dialog, but if your project is getting more complex (more dialog box) you will need it.
- UsbHidIO.cpp. This is the source file that contains the
CUsbHidIO class. This code includes every function that is going to be needed to make HID interface so simple. You should not need to modify anything of it, just keep it like that, but if you see something wrong let me know.
- RobotDogD1DLG.cpp. This file contains the sample code about how to use the
The Class CUsbHidIO
CUsbHidIO class has the following methods:
The List of Devices: GetHIDCollectionDevices()
This is the most important function. All the parameters are output ones. This is the first function that you need to run. When running, it scans your computer searching for all the HID devices plugged into it. Every time that a new HID device is found, all their important information is stored in 3 different arrays.
This array contains every internal path to every device data available and plugged into the computer. This “
path” identifies every one of the HID device (keyboard, mouse, joysticks, gamepad,…). The path will be the same even if the device is unplugged and plug it back later on. The path obtained will be used by the rest of the functions.
This array contains every
ProductID of every
HID device. Since the position in the array is the same, these parameters can be used to obtain from
aDetailData the “path” of the
ProductID device that we are looking for.
Thanks to this array, we can get a lot of information from each one of the devices such as the number of analog values, buttons or LED that it has implemented.
Getting the Capabilities of the Devices: GetHIDValueCaps()
This function is used to get all the capabilities and data about all the analog values that are implemented in one of the devices identified before with the function
CUsbHidIO::GetHIDCollectionDevices and that were stored in the array
aDetailData[index]. Actually, this function is calling the routine
DWORD GetHIDValueCaps (
PHIDP_VALUE_CAPS ValueCaps, USHORT &ValueCapsLength,
DWORD GetHIDButtonCaps (
PHIDP_BUTTON_CAPS ButtonCaps, USHORT &ButtonCapsLength,
This parameter must be set to point to an only one of the devices detected before.
HIDP_REPORT_TYPE enumerator value that identifies the report type. It is usually set to
Pointer to a caller-allocated buffer (an array) in which the function returns the list of
HIDP_VALUE_CAPS structures implemented and readable in the device.
LogicalMax, etc. are data included in this structure that will provide good information about each one of the analog values implemented into the device.
The number of elements (structures) added into the array/buffer of
This parameter could be
NULL or could be the handle provided for
CreateFile() function when opening the
Device(aDetailData.path). If you set the value
NULL, the handle is created inside, used, and destroyed before leaving the function. Creating the
Handle outside the function will be better for system.
Quite similar to
CUsbHidIO:: GetHIDValueCaps but for Buttons (digital inputs).
Reading Inputs: Axis, Values, Buttons: GetHIDUsagesValues(), GetHIDButtonState()
This function is used to capture, at the same time, all the analog values (Axis) that are implemented in one of the devices identified before and that were stored in the array
aDetailData[index]. Actually this function is calling the routine
ValueCapsLength analog values.
ValueCapsLength are parameters captured by
DWORD GetHIDUsagesValues (
PHIDP_VALUE_CAPS ValueCaps, USHORT ValueCapsLength,
PULONG UsageValue,DWORD WaitForMsc, __timeb64* pAdquiredAt,
DWORD GetHIDButtonState (
USAGE UsagePage, PUSAGE UsageList, PULONG UsageLength,
DWORD WaitForMsc, __timeb64* AdquiredAt,
Pointer to a caller-allocated buffer (an array of
ValueCapsLength elements) in which the function returns a list of analog values captured from the device analog inputs.
Every analog value is read from reports that usually are sent for device continuously, but some devices only send reports if at least one value changes. If nothing changes, no report will be sent. This could be a problem so we have two different possibilities to minimize it. The first one is waiting for the report. This value for waiting is set in milliseconds by
WaitForMsc. The second possibility is to create a different thread for the reading.
If the function returns a good reading, then they will came with the time when they will get them. If the function does not return
HIDP_STATUS_SUCCESS the parameter
pAdquiredAt must not be read.
This function is quite similar to
GetHIDUsagesValues(). This is a call to the routine
Pointer to a caller-allocated buffer that the function uses to return the usages of all buttons that are set to ON and belong to the usage page specified by
Setting Outputs to ON/OFF: SetHIDLEDState(), SendHIDReport()
This function is used to set the LEDs state to ON or OFF. Although
DirectInput control (Force Feedback, Rumble) is typically the way used in Joysticks for Motor Control, there is another way for motor control, using HID routines about LEDs control. It doesn't matter if you are going to switch ON a LED or a Motor, it is just a matter of power. Actually, this function calls the routine
DWORD SetHIDLEDState (
USAGE UsagePage, PUSAGE UsageList, PULONG UsageLength,
DWORD SendHIDReport (
Pointer to a caller-allocated buffer that the function uses to set the usages that must be set to ON and belongs to the usage page specified by
UsagePage parameter. If
UsagePage is set to
0x08, then Usages makes reference to LEDs.
In this function, it is usually set to
CUsbHidIO:: SendHIDReport ()
This function is used to set a raw “report” to the device. Actually, this function calls the routine
WriteFile(). The report will be truncated inside the routine to the length of the parameter “
The Application Sample: RobotDogD1DLG.cpp
This section of the code was developed under MFC (Microsoft Foundation Class), this allows more simplicity of code.
The picture below shows the main dialog.
Reading Axis (Analog values)
1st - Getting the list of devices
Click on button [Load HID List].
To get the list of the devices plugged into the system, we need to call
GetHIDCollectionDevices(). This can be done only once, just on loading the application, but I add
Main_OnDeviceChange() to get the possibility of updating the list just after plugging or unplugging any HID device into the system.
2nd - Getting the device capabilities
Click on button [Read Value] and it will be called the method
This can be done just one time, just after selecting your device.
This sample uses manual device selection by clicking the mouse over one of the devices listed in the "HID Devices"
ListBox, but usually you should select it automatically by comparing
3rd - Reading the values
Click on button [Read Value].
In this example, we made a call to the method
.GetHIDUsagesValues(), but it could be better if you implement this method inside a different thread and even more accurate if you create your own handle to the device selected.
mValueCaps = (PHIDP_VALUE_CAPS)calloc (MyDevCaps.NumberInputValueCaps,
Result = mUsbHidIO.GetHIDValueCaps (MyDevPath ,HidP_Input,mValueCaps,numValues,NULL);
aUsageValue =(PULONG)calloc (numValues, sizeof (ULONG));
Result = mUsbHidIO.GetHIDUsagesValues (MyDevPath, HidP_Input,
mValueCaps, numValues, aUsageValue, WaitForMsc, &AdquiredAt,NULL);
if (Result == HIDP_STATUS_SUCCESS)
ctime_s( timeline, 26, & (AdquiredAt.time ) );
csAdquiredAt = _T(timeline);
csMilliseconds.Format("%u", AdquiredAt.millitm );
csAdquiredAt = _T("-:-");
csMilliseconds = _T("-");
The rest of the code is quite similar, contact me if you have any doubts.
- Jan Axelson is a very good reference and much better and "solid" code. There is also a good example in the Windows DDK code source sample: "
Now it is ready for: Visual Studio 10 and it runs in Windows 7.