USB Client - Generic HID

has anyone created a Generic HID device?

i just want one with 64 byte in / out report 0… nothing more that that

Hid, with all its descriptors is confusing!

We recommended using CDC:

The following has an example of what you are trying to do but you need to handle Windows drivers…

YEA… i want to avoid a comport showing on ones system…

that example for the other one, is not HID… that would for like for winusb or libusb

im looking for a straight HID… i have hacked some code together… and i got it to enunmerate as HID
but im still not sure if i got descriptors right, do i need a SET REPORT discriptor?
if i had source code to like the HID mouse, i might be able to hack though

this is my start:

using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.USBClient;
using GHIElectronics.NETMF.Hardware;

using MS = Microsoft.SPOT.Hardware.UsbClient;

namespace USBClient_Example
	public class Program
		const int MAXPOWER_MILLIAMPS = 280; // default; can be up to 500mA

		const int HID_DESCRIPTOR_TYPE = 0x21;

		public static void Main()

			OutputPort LED;
			LED = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.LED, true);

			// Check debug interface
			if (Configuration.DebugInterface.GetCurrent() == Configuration.DebugInterface.Port.USB1)
				throw new InvalidOperationException("Current debug interface is USB. It must be changed to something else before proceeding. Refer to your platform user manual to change the debug interface.");

			ushort myVID = 0x1234;
			ushort myPID = 0x0001;
			ushort myDeviceVersion = 0x100;
			ushort myDeviceMaxPower = 250; // in milli amps
			string companyName = "My Company";
			string productName = "My Product";
			string myDeviceSerialNumber = "0";

			// Create the device. Assume it just has one read and one write endpoints.
			USBC_Device device = new USBC_Device(myVID, myPID, myDeviceVersion, myDeviceMaxPower, companyName, productName, myDeviceSerialNumber);
			// Endpoints
			byte writeEPNumber = device.ReserveNewEndpoint();
			byte readEPNumber = device.ReserveNewEndpoint();
			MS.Configuration.Endpoint[] epDesc = {
                new MS.Configuration.Endpoint(writeEPNumber, MS.Configuration.Endpoint.ATTRIB_Write | MS.Configuration.Endpoint.ATTRIB_Interrupt),
                new MS.Configuration.Endpoint(readEPNumber, MS.Configuration.Endpoint.ATTRIB_Read | MS.Configuration.Endpoint.ATTRIB_Interrupt),
			epDesc[0].wMaxPacketSize = 64;
			epDesc[0].bInterval = 1;
			epDesc[1].wMaxPacketSize = 64;
			epDesc[1].bInterval = 1;
			// Class Descriptor
			byte[] hidClassDescriptorPayload = new byte[] 
                0x01, 0x01,     // bcdHID (v1.01)
                0x00,           // bCountryCode
                0x01,           // bNumDescriptors
                0x22,           // bDescriptorType (report)
                0x22, 0x00      // wDescriptorLength (report descriptor size in bytes)
			// HID class descriptor
			MS.Configuration.ClassDescriptor hidClassDescriptor = new MS.Configuration.ClassDescriptor(HID_DESCRIPTOR_TYPE, hidClassDescriptorPayload);
			// Interface
			MS.Configuration.UsbInterface usbInterface = new MS.Configuration.UsbInterface(0, epDesc);
			usbInterface.classDescriptors = new MS.Configuration.ClassDescriptor[] { hidClassDescriptor };
			usbInterface.bInterfaceClass    = 0x03; // HID
			usbInterface.bInterfaceSubClass = 0x00;
			usbInterface.bInterfaceProtocol = 0x00;
			// Attach interface
			byte interfaceIndex = device.AddInterface(usbInterface, "My interface name");
			// HID report descriptor
			byte[] hidGenericReportDescriptorPayload = new byte[]
                0x06, 0xA0, 0xFF,   // Usage Page (vendor-defined)
                0x09, 0x01,         // Usage (vendor-defined)

                0xA1, 0x01,         // Collection (Application)

                0x09, 0x03,         // Usage (vendor-defined)
                0x15, 0x00,         // Logical Minimum (0)
                0x26, 0xFF, 0x00,   // Logical Maximum (255)
                0x75, 0x08,         // Report Size (8 bits)
                0x95, 0x40,         // Report Count (64)
                0x81, 0x02,         // Input (Data, Variable, Absolute)

                0x09, 0x04,         // Usage (vendor-defined)
                0x15, 0x00,         // Logical Minimum (0)
                0x26, 0xFF, 0x00,   // Logical Maximum (255)
                0x75, 0x08,         // Report Size (8 bits)
                0x95, 0x40,         // Report Count (64)
                0x91, 0x02,         // Output (Data, Variable, Absolute)

                0xC0                // End Collection
			MS.Configuration.GenericDescriptor hidGenericReportDescriptor = new MS.Configuration.GenericDescriptor(0x81, 0x2200, hidGenericReportDescriptorPayload);
			hidGenericReportDescriptor.bRequest = 0x06; // GET_DESCRIPTOR
			hidGenericReportDescriptor.wIndex   = 0x00; // INTERFACE 0 (zero)
			// attach descriptor

			// Strings
			MS.Configuration.StringDescriptor stringDescriptor1 = new MS.Configuration.StringDescriptor(1, "String 1");
			MS.Configuration.StringDescriptor stringDescriptor2 = new MS.Configuration.StringDescriptor(2, "String 2");

			// This is used for reading and writing
			USBC_Stream stream = device.CreateUSBStream(writeEPNumber, readEPNumber);

			// All done, you can start the device now

			byte[] buffer = new byte[100];
			int bytesRead = 0;

			while (true)
				// Check if connected to PC
				if (USBClientController.GetState() != USBClientController.State.Running)
					Debug.Print("Waiting to connect to PC...");

					bytesRead = stream.Read(buffer, 0, buffer.Length);

					if (bytesRead > 0)

					// Read and write using the stream
					//stream.Write(new byte[] { 0, (byte)'H', (byte)'E', (byte)'L', (byte)'L', (byte)'O' }, 0, 6);




like if i write to the stream some bytes
if the 1st byte is 0, is that the report id?
i just dont know how to manage it

when i have my write ther of 0, hello… it does come into the pc
when i view with usb analyzer, but my hid pc code doesnt pick it up when i say readreport

i got data going!!!

back and fourth… seems i dont need to have the 1st byte as a 0

if (bytesRead > 0)
	String s = new String(System.Text.Encoding.UTF8.GetChars(buffer));

	if (s.ToLower() == "hello")
		byte[] toSend = new byte[64];
		byte[] mytext = System.Text.UTF8Encoding.UTF8.GetBytes("How Are You?");
		Array.Copy(mytext, toSend, mytext.Length);
		stream.Write(toSend, 0, toSend.Length);

THE 1st byte in the pc side has a 0, so its report id 0 :slight_smile:
my guess since i only defined one report for in and out, its 0
and there prob is some other action you have to do to switch report ids

Glad it is working for you.
You can share wiki page/code for generic HID device on our website.

Maybe here GHI Electronics – Where Hardware Meets Software

I am almost trying the same thing! I want to emulate a HID pen or multitouch device.
Thanks for showing us your code.

Do you mind if I use parts of it?

you can use it all… now that i got it working im going to improve it
im new at this NETMF and USB…while i have done a bunch of embedding programming
ive been afraid of USB… but c# and netmf took alot of that away
so im gonna continue to work on this… and try to get some threading involved
i guess i would have hoped there was already some events for when usb data is received
but im sure ill figure it out…
its all play time for me on this right now…

Thank you for sharing.

I tried to change the report descriptor and my FEZ gets detected as a Pen device now.

            byte[] hidGenericReportDescriptorPayload = new byte[]
               0x05,0x0d,                         // USAGE_PAGE (Digitizers)
    0x09, 0x04,                         // USAGE (Touch Screen)
    0xa1, 0x01,                         // COLLECTION (Application)
    0x85, REPORTID_TOUCH,               //   REPORT_ID (Touch)
    0x09, 0x20,                         //   USAGE (Stylus)
    0xa1, 0x00,                         //   COLLECTION (Physical)
    0x09, 0x42,                         //     USAGE (Tip Switch)
    0x15, 0x00,                         //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                         //     LOGICAL_MAXIMUM (1)
    0x75, 0x01,                         //     REPORT_SIZE (1)
    0x95, 0x01,                         //     REPORT_COUNT (1)
    0x81, 0x02,                         //     INPUT (Data,Var,Abs)
    0x95, 0x03,                         //     REPORT_COUNT (3)
    0x81, 0x03,                         //     INPUT (Cnst,Ary,Abs)
    0x09, 0x32,                         //     USAGE (In Range)
    0x09, 0x47,                         //     USAGE (Confidence)
    0x95, 0x02,                         //     REPORT_COUNT (2)
    0x81, 0x02,                         //     INPUT (Data,Var,Abs)
    0x95, 0x0a,                         //     REPORT_COUNT (10)
    0x81, 0x03,                         //     INPUT (Cnst,Ary,Abs)
    0x05, 0x01,                         //     USAGE_PAGE (Generic Desktop)
    0x26, 0xff, 0x7f,                   //     LOGICAL_MAXIMUM (32767)
    0x75, 0x10,                         //     REPORT_SIZE (16)
    0x95, 0x01,                         //     REPORT_COUNT (1)
    0xa4,                               //     PUSH
    0x55, 0x0d,                         //     UNIT_EXPONENT (-3)
    0x65, 0x00,                         //     UNIT (None)
    0x09, 0x30,                         //     USAGE (X)
    0x35, 0x00,                         //     PHYSICAL_MINIMUM (0)
    0x46, 0xff, 0x7f,                   //     PHYSICAL_MAXIMUM (32767)
    0x81, 0x02,                         //     INPUT (Data,Var,Abs)
    0x09, 0x31,                         //     USAGE (Y)
    0x46, 0xff, 0x7f,                   //     PHYSICAL_MAXIMUM (32767)
    0x81, 0x02,                         //     INPUT (Data,Var,Abs)
    0xb4,                               //     POP
    0x05, 0x0d,                         //     USAGE PAGE (Digitizers)
    0x09, 0x48,                         //     USAGE (Width)
    0x09, 0x49,                         //     USAGE (Height)
    0x95, 0x02,                         //     REPORT_COUNT (2)
    0x81, 0x02,                         //     INPUT (Data,Var,Abs)
    0x95, 0x01,                         //     REPORT_COUNT (1)
    0x81, 0x03,                         //     INPUT (Cnst,Ary,Abs)
    0xc0,                               //   END_COLLECTION
    0xc0,                               // END_COLLECTION


I have one more question now. How can I know how much byte each report I send must contain? And what’s the offset of the tip switch, X and Y in the report?

Hope you can help with this.

i cant, i think im far away from getting reports working right
at this point i have what id call Raw HID

im going to think, that a USB Request will come from the host at the intervals setup in one of the descriptors

i still dont fully understand HID… its very complicated to me

When I look at the MS mouse example (see your documents, microframework dir.), its an easy descriptor to understand. They don’t use polling. They just send a report as defined by the report descriptor at a given interval. You can almost read how many bits they define for what (reportsize in number of bits multiplied by reportcount)

But the descriptor for the pen device is to complicated for me :slight_smile:

In your case you should send a report of 2x64 bytes. So send an array of 128bytes to the stream. That should work for your descriptor.

i thought the host would ask me for the report… at the interval…
it seems not…
(i dont fully understand HID)

and i have no issues sending the 64byte reports
when i do the pc gets em…

maybe when we send, aka write to the stream, they do really leave the micro till
the usb host asks for them


@ Wouter
Reading the description, here is what your report should look like:

Describes one bit (Tip Switch)

Then 3 constant bits (not used, usually padding to align bits)

One bit (In Range) and then one bit (Confidence)

10 bits constant. Note that you have 16 bits so far so 2 bytes.

Then one 16 bit value for X.
and so on.

@ mitch: oh, didn’t see you had input and output in the descriptor, thought they were both input. Idd 64bytes is correct.

@ Mike: thanks, I’ve got it to work. And I was able to ‘read’ and understand the descriptor now :slight_smile: