Click here to Skip to main content
15,868,016 members
Articles / Desktop Programming / MFC

HID Application Class for Easy Reading of Joystick and Robotic Sensors

Rate me:
Please Sign up or sign in to vote.
4.88/5 (13 votes)
22 Feb 2013LGPL38 min read 94.2K   11.5K   61   35
Class for reading HID devices like joystick or gamepad - very simple, to be used for robotics
Sample Image - maximum width is 600 pixels

Introduction

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.
  • The CUsbHidIO class, makes heavy internal use of “handles” and “memory allocation”, so try do not run pointers. It is better using mypointer[i] than mypointer++.
  • 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

The Files

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 CUsbHidIO class.

The Class CUsbHidIO

CUsbHidIO class has the following methods:

  • GetHIDCollectionDevices()
  • GetHIDValueCaps()
  • GetHIDButtonCaps()
  • GetHIDUsagesValues()
  • GetHIDButtonState()
  • SetHIDLEDState()
  • SendHIDReport()

The List of Devices: GetHIDCollectionDevices()

CUsbHidIO::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.

C++
DWORD GetHIDCollectionDevices(
    USHORT &NumHIDDevices, 
    PSP_DEVICE_INTERFACE_DETAIL_DATA aDetailData[RD_MAXHIDDEVICES], 
    PHIDD_ATTRIBUTES aAttributes,
    HIDP_CAPS aNumValueCaps[RD_MAXHIDDEVICES]); 
aDetailData[index]

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.

aAttributes[index]

This array contains every VendorID and 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 VendorID-ProductID device that we are looking for.

aNumValueCaps

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()

CUsbHidIO:: 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 HidP_GetValueCaps().

C++
DWORD GetHIDValueCaps (
    PSP_DEVICE_INTERFACE_DETAIL_DATA aDetailData,
    HIDP_REPORT_TYPE ReportType,
    PHIDP_VALUE_CAPS ValueCaps, USHORT &ValueCapsLength,
    HANDLE DeviceHandle);

DWORD GetHIDButtonCaps (
    PSP_DEVICE_INTERFACE_DETAIL_DATA aDetailData,
    HIDP_REPORT_TYPE ReportType,
    PHIDP_BUTTON_CAPS ButtonCaps, USHORT &ButtonCapsLength,
    HANDLE DeviceHandle); 
aDetailData

This parameter must be set to point to an only one of the devices detected before.

ReportType

Specifies a HIDP_REPORT_TYPE enumerator value that identifies the report type. It is usually set to HidP_Input.

ValueCaps

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. UsagePage, Usage, LogicalMin, LogicalMax, etc. are data included in this structure that will provide good information about each one of the analog values implemented into the device.

ValueCapsLength

The number of elements (structures) added into the array/buffer of ValueCaps.

DeviceHandle

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.

CUsbHidIO::GetHIDButtonCaps()

Quite similar to CUsbHidIO:: GetHIDValueCaps but for Buttons (digital inputs).

Reading Inputs: Axis, Values, Buttons: GetHIDUsagesValues(), GetHIDButtonState()

CUsbHidIO:: GetHIDUsagesValues()

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 HidP_GetUsageValue() with ValueCapsLength analog values.

aDetailData, ReportType, ValueCaps, and ValueCapsLength are parameters captured by GetHIDCollectionDevices() and GetHIDValueCaps().

C++
DWORD GetHIDUsagesValues (
    PSP_DEVICE_INTERFACE_DETAIL_DATA aDetailData,
    HIDP_REPORT_TYPE ReportType,
    PHIDP_VALUE_CAPS ValueCaps, USHORT ValueCapsLength,
    PULONG UsageValue,DWORD WaitForMsc, __timeb64* pAdquiredAt,
    HANDLE ReadHandle);
DWORD GetHIDButtonState (
    PSP_DEVICE_INTERFACE_DETAIL_DATA aDetailData,
    HIDP_REPORT_TYPE ReportType,
    USAGE UsagePage, PUSAGE UsageList, PULONG UsageLength,
    DWORD WaitForMsc, __timeb64* AdquiredAt,
    HANDLE ReadHandle);
UsageValue

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.

WaitForMsc

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.

pAdquiredAt

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.

CUsbHidIO:: GetHIDButtonState()

This function is quite similar to GetHIDUsagesValues(). This is a call to the routine HidP_GetUsages().

UsageList

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 UsagePage parameter.

Setting Outputs to ON/OFF: SetHIDLEDState(), SendHIDReport()

CUsbHidIO:: SetHIDLEDState()

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 HidP_SetUsages().

C++
DWORD SetHIDLEDState (
    PSP_DEVICE_INTERFACE_DETAIL_DATA aDetailData,
    HIDP_REPORT_TYPE ReportType,
    USAGE UsagePage, PUSAGE UsageList, PULONG UsageLength,
    HANDLE WriteHandle);

DWORD SendHIDReport (
    PSP_DEVICE_INTERFACE_DETAIL_DATA aDetailData,
    PCHAR pReport,
    HANDLE tmpWriteHandle); 
UsageList

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.

ReportType

In this function, it is usually set to HidP_Output.

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 “OutputReportByteLength”.

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)

Steps

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 .GetHIDValueCaps().

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 VendorID/ProductID.

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.

Sample Image - maximum width is 600 pixels

C++
// allocate memory for the list of capabilities
mValueCaps = (PHIDP_VALUE_CAPS)calloc (MyDevCaps.NumberInputValueCaps, 
        sizeof (HIDP_VALUE_CAPS));

// calling the class to get all the capabilities of the device selected
Result = mUsbHidIO.GetHIDValueCaps (MyDevPath ,HidP_Input,mValueCaps,numValues,NULL);

// allocate memory for the list of values
aUsageValue =(PULONG)calloc (numValues, sizeof (ULONG));

// calling the class to get all the values of the device selected
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 );
}
else
{
    csAdquiredAt = _T("-:-");
    csMilliseconds = _T("-");
}

The rest of the code is quite similar, contact me if you have any doubts.

References

  • 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: "HCLIENT".

History

  • First revision

Now it is ready for: Visual Studio 10 and it runs in Windows 7.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Engineer --
Spain Spain
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionNeed to use this program to read the mouse click function for mouse Pin
kstang30-Jul-19 0:34
kstang30-Jul-19 0:34 
QuestionHID over WiFi Pin
Mehrdad_K20-Aug-18 21:52
professionalMehrdad_K20-Aug-18 21:52 
QuestionI am looking for such a class for DELPHI XE6 for Android 4.4+ Pin
Member 1240520620-Mar-16 8:24
Member 1240520620-Mar-16 8:24 
QuestionAcronyms Pin
Rick York20-Feb-13 10:26
mveRick York20-Feb-13 10:26 
AnswerRe: Acronyms Pin
juancaruca13-Mar-13 8:55
juancaruca13-Mar-13 8:55 
GeneralRe: Acronyms Pin
Rick York4-Apr-13 21:17
mveRick York4-Apr-13 21:17 
GeneralARTICLE DISCONTINUED Pin
juancaruca12-Jan-14 8:08
juancaruca12-Jan-14 8:08 
QuestionCompiler Error Pin
Member 89233133-May-12 10:02
Member 89233133-May-12 10:02 
AnswerRe: Compiler Error Pin
juancaruca4-May-12 6:01
juancaruca4-May-12 6:01 
GeneralRe: Compiler Error Pin
Member 89233134-May-12 7:18
Member 89233134-May-12 7:18 
GeneralRe: Compiler Error Pin
juancaruca4-May-12 10:15
juancaruca4-May-12 10:15 
GeneralRe: Compiler Error Pin
Member 89233137-May-12 7:09
Member 89233137-May-12 7:09 
GeneralRe: Compiler Error Pin
juancaruca8-May-12 8:25
juancaruca8-May-12 8:25 
GeneralRe: Compiler Error Pin
Member 89233139-May-12 5:04
Member 89233139-May-12 5:04 
GeneralRe: Compiler Error Pin
juancaruca9-May-12 9:38
juancaruca9-May-12 9:38 
GeneralRe: Compiler Error Pin
Member 892331310-May-12 0:41
Member 892331310-May-12 0:41 
GeneralRe: Compiler Error Pin
juancaruca10-May-12 7:48
juancaruca10-May-12 7:48 
GeneralRe: Compiler Error Pin
Member 892331310-May-12 8:12
Member 892331310-May-12 8:12 
GeneralRe: Compiler Error Pin
juancaruca22-May-12 8:43
juancaruca22-May-12 8:43 
GeneralRe: Compiler Error Pin
juancaruca18-Oct-12 7:32
juancaruca18-Oct-12 7:32 
GeneralRe: Compiler Error Pin
BrianCharles28-Jun-13 12:16
BrianCharles28-Jun-13 12:16 
QuestionSending siganls to joystick Pin
jockerabc24-Oct-11 9:44
jockerabc24-Oct-11 9:44 
AnswerRe: Sending siganls to joystick Pin
juancaruca25-Oct-11 4:19
juancaruca25-Oct-11 4:19 
GeneralRe: Sending siganls to joystick Pin
jockerabc25-Oct-11 6:38
jockerabc25-Oct-11 6:38 
Questionlinker errors trying to use your code Pin
poorterp22-Aug-11 10:31
poorterp22-Aug-11 10:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.