Part 1 - Reference Hardware
Part 2 - ATmega32U4 Firmware, Demonstration Firmware, Windows Applications
USB Generic HID C# Communication Library
The USB communication and USB specific functionality (such as device attach and detach detection) is provided by the USB Generic HID Communication Library which compiles as a DLL under Visual Studio 2010. This means the library can be quickly and easily included in any C# project which required Generic HID communication capabilities.
The communication library also provides extensive debugging output when compiled in debug mode. This allows you to quickly and easily track issues when developing.
Using the class library
Once you have included the class library project in your solution (please look at the provided applications to see how this is done) you will then need to add some lines of C# code into your main form in order to set up and use the libraries functions as well as defining the interface to your USB device.
Firstly you will need to create a class which represents your USB device and provides the methods to talk to it. You simply add a class to your project and name it after your USB device. In the demonstration application this class is called usbDemoDevice.cs. The class constructor is derived from the device constructor from the library and you can place any required initialisation commands in it; here is the example from the demo application:
class usbDemoDevice : WFF_GenericHID_Communication_Library
{
public usbDemoDevice(int vid, int pid) : base(vid, pid)
{
// Initialise the local copy of the device's state
deviceState.led1State = false;
deviceState.led2State = false;
deviceState.led3State = false;
deviceState.led4State = false;
deviceState.button1State = false;
deviceState.button2State = false;
deviceState.potState = 0;
}
Then you should provide a method for each command supported by your firmware (although you can use a single method to call many USB commands if required this would probably indicate that you have too many commands which should be optimised). Here is an example from the demo application which creates the 64 byte packet and then adds in the required command byte and parameter bytes before writing the report to the device (a report is the USB term for the packet):
public bool setDeviceStatus()
{
// Command 0x81 - Set device status
Debug.WriteLine(
"Demo Application -> Sending set device status command (0x81) to USB device");
// Declare our output buffer
Byte[] outputBuffer = new Byte[64];
// Byte 0 must be set to our command
outputBuffer[0] = 0x81;
// Set the packet data according to the local LED status
if (deviceState.led1State == true) outputBuffer[1] = 1; else outputBuffer[1] = 0;
if (deviceState.led2State == true) outputBuffer[2] = 1; else outputBuffer[2] = 0;
if (deviceState.led3State == true) outputBuffer[3] = 1; else outputBuffer[3] = 0;
if (deviceState.led4State == true) outputBuffer[4] = 1; else outputBuffer[4] = 0;
// Perform the write command
bool success;
success = writeSingleReportToDevice(outputBuffer);
return success;
}
Once you have your USB device class and methods defined it's time to go back to you main form and add in some code to invoke and use the device class. Firstly you need to place some code into the main forms constructor method to initialise the device when the form is loaded and then you need to create an instance of your USB device, here is the example from the demo application:
public mainForm()
{
InitializeComponent();
// ATmega32U4 example firmware uses VID=0x03EB and PID=0x2150
theUsbDemoDevice = new usbDemoDevice(0x03EB, 0x2150);
// Add a listener for usb events
theUsbDemoDevice.usbEvent += new usbDemoDevice.usbEventsHandler(usbEvent_receiver);
// Perform an initial search for the target USB device (in case
// it is already connected as we will not get an event for it)
theUsbDemoDevice.findTargetDevice();
}
Next you have to create a listener for USB events which are captured by the library. These events inform your application when the target USB device is added or removed from the host. Here is a simple example of such as listener method:
// Create a listener for USB events
private void usbEvent_receiver(object o, EventArgs e)
{
// Check the status of the USB device and update the form accordingly
if (theUsbDemoDevice.isDeviceAttached)
{
// USB Device is currently attached
// Update the form's status label
this.usbDeviceStatusLabel.Text = "USB Device is attached";
}
else
{
// USB Device is currently unattached
// Update the form's status label
this.usbDeviceStatusLabel.Text = "USB Device is detached";
}
}
After this all that is left is to add some controls to your form which trigger the various supported commands to and from your USB device. This is very application specific, however both provided applications should show clearly how this is achieved.
Class Library Methods
The USB Generic HID library provides a simple interface to the underlying complexity of USB communications in Windows. The library is deliberately designed to be as simple as possible to program with. Here are the public methods which are supported and their function:
void findTargetDevice() - This method fetches a list of the currently attached HID devices, then it searches through the list looking for a HID device with the correct VID and PID for the target device. It is used to discover a device which may already be plugged in when the application initialises.
void usbEventsHandler(object sender, EventArgs e) - The usbEventHandler method allows the application to listen for events which are cause by the target USB device (attach and detach events).
WFF_GenericHID_Communication_Library(int vid, int pid) - This constructor method creates an object for HID communication and attempts to find (and bind to) the USB device indicated by the passed VID (Vendor ID) and PID (Product ID) which should both be passed as unsigned integers. This is the base class to be used when creating a class for your own USB device as described above.
bool isDeviceAttached - This method provides a simple mechanism for the application to check if the USB device is still attached. It returns true if the device is available and false if it is not.
bool writeSingleReportToDevice(Byte[] outputReportBuffer) - This method writes a single report to the device. The outputReportBuffer must be the correct length for the report (which set at the maximum value of 64 bytes in the example applications). It returns true on success and false on failure.
bool writeMultipleReportsToDevice(Byte[] outputReportBuffer, int numberOfReports) - This method writes multiple reports to the device in a single operation. The outputReportBuffer must be 64 byte times the number of reports to be sent. It returns true on success and false on failure.
bool readSingleReportFromDevice(ref Byte[] inputReportBuffer) - This method reads a single report from the device and places it in the referenced inputReportBuffer (which must be 64 bytes). It returns true on success and false on failure.
bool readMultipleReportsFromDevice(ref Byte[] inputReportBuffer, int numberOfReports) - This method reads multiple reports from the device in a single operation. The inputReportBuffer must be 64 byte times the number of reports to be read. It returns true on success and false on failure.
Licensing
The Generic HID library, firmware and example applications are published under the OSI approved MIT license which is a very permissive open-source license allowing the use of the project code in both open and closed source application both commercially and non-commercially. The only requirement is attribution, i.e. your code and/or documentation should contain a statement such as 'Contains portions of the WFF GenericHID Communication Library which is 2011 Simon Inns - http://www.waitingforfriday.com'.