USBHID bindings

Purpose of this webpage

The purpose of this webpage is to develop programs:

  • which are able to send and read raw data from an mbed HID device.
  • in different programming languages
  • running on different platforms

USBHID bindings status

LanguageWindowsLinuxMac OS
PythonYesYes
Cyesyesyes
C++yes*yes*yes*
Javayes
C#yes
Others

*using C bindings

Mbed test case code

On the mbed, we want to test that we are able to:

  • send raw data
  • receive raw data

For this a simple program has been developed:

#include "mbed.h"
#include "USBHID.h"

//We declare a USBHID device. Input out output reports have a length of 8 bytes
USBHID hid(8, 8);

//This report will contain data to be sent
HID_REPORT send_report;
HID_REPORT recv_report;

Serial pc(USBTX, USBRX);

int main(void) {
    send_report.length = 8;

    while (1) {
        //Fill the report
        for (int i = 0; i < send_report.length; i++) {
            send_report.data[i] = rand() & 0xff;
        }
            
        //Send the report
        hid.send(&send_report);
        
        //try to read a msg
        if(hid.readNB(&recv_report)) {
            pc.printf("recv: ");
            for(int i = 0; i < recv_report.length; i++) {
                pc.printf("%d ", recv_report.data[i]);
            }
            pc.printf("\r\n");
        }
        
        wait(0.1);
    }
}

Import programUSBHID_TestCase

USBHID test case

Computer side program

To validate the test case and write a Yes in the USBHID bindings status ;), a program has to prove that it is able to:

  • Read data from the mbed
  • Send data to the mbed

Example in Python on Windows

On Windows I use pywinusb to communicate with the mbed

Code

#
#Simple example on how to send and receive data to the Mbed over USB (on windows) using pywinusb
#
import pywinusb.hid as hid
from time import sleep
import random
	
# handler called when a report is received
def rx_handler(data):
    print 'recv: ', data

def findHIDDevice(mbed_usage, mbed_vendor_id):
    # Find all devices connected
    all_devices = hid.HidDeviceFilter(vendor_id = mbed_vendor_id).get_devices()
    
    if not all_devices:
        print "No device connected"
    else:
        # search for the Mbed
        for HIDdevice in all_devices:
            try:
                HIDdevice.open()
                # browse output reports
                for report in HIDdevice.find_output_reports():
                    if mbed_usage in report:

                        #MBED found
                        print 'Mbed detected'

                        #Attach a custom handler when a data is received
                        HIDdevice.set_raw_data_handler(rx_handler)
                        
                        #send a report each 0.2 second. The report is a random array of 8 bytes
                        while True:
                            for i in range(8):
                                report[mbed_usage][i] = random.randint(0, 255)
                            report.send()
                            sleep(0.2)
            except:
                print 'close'
                HIDdevice.close()
		

if __name__ == '__main__':
    # The vendor ID used in the Mbed program
    mbed_vendor_id = 0x1234 

    # Vendor page and usage_id = 2
    mbed_usage = hid.get_full_usage_id(0xffab, 0x02)

    # Search the Mbed, attach rx handler and send data
    findHIDDevice(mbed_usage, mbed_vendor_id)

Demo

/media/uploads/samux/hid_mbed1.png /media/uploads/samux/py_hid1.png

  • On the left screenshot, we can see data received by the mbed: a random array containing 8 values as expected.
  • On the right screenshot, we can see values sent by the mbed to the python script. The last 8 values printed are random values sent by the mbed. The first byte represents the report_id. As there is not report_id for this USB device, by default, there is 0.

Example in Python on Linux

On Linux, you can use PyUSB 1.0 (still in alpha as of Nov 2012)

Code

#
#Simple example on how to send and receive data to the Mbed over USB (on Linux) using pyUSB 1.0
#
import os
import sys

import usb.core
import usb.util

from time import sleep
import random

# handler called when a report is received
def rx_handler(data):
	print 'recv: ', data

def findHIDDevice(mbed_vendor_id, mbed_product_id):
	# Find device
	hid_device = usb.core.find(idVendor=mbed_vendor_id,idProduct=mbed_product_id)
	
	if not hid_device:
		print "No device connected"
	else:
		sys.stdout.write('mbed found\n')
		if hid_device.is_kernel_driver_active(0):
			try:
				hid_device.detach_kernel_driver(0)
			except usb.core.USBError as e:
				sys.exit("Could not detatch kernel driver: %s" % str(e))
		try:
			hid_device.set_configuration()
			hid_device.reset()
		except usb.core.USBError as e:
			sys.exit("Could not set configuration: %s" % str(e))
		
		endpoint = hid_device[0][(0,0)][0]		
		
		while True:
			data = [0x0] * 16
						
			#read the data
			bytes = hid_device.read(endpoint.bEndpointAddress, 8)
			rx_handler(bytes);
			
			for i in range(8):
				data[i] = bytes[i]
				data[i+8] = random.randint(0, 255)

			hid_device.write(1, data)

if __name__ == '__main__':
	# The vendor ID and product ID used in the Mbed program
	mbed_vendor_id = 0x1234 
	mbed_product_id = 0x0006
 
	# Search the Mbed, attach rx handler and send data
	findHIDDevice(mbed_vendor_id, mbed_product_id)

Demo

/media/uploads/crazystick/usb-linux-2.png /media/uploads/crazystick/usb-mbed-2.png

  • on the left, the data received by the Python program in Linux
  • on the right, the data received by the mbed (the Python program echos back the data received from the mbed, and adds its own random 8 bits

Example in Java on Windows

Use the following UsbHid.java class to communicate with the Mbed.

You will need jna.jar https://github.com/twall/jna and hidapi.dll http://www.signal11.us/oss/hidapi/hidapi/doxygen/html/hidapi_8h.html

Download this file UsbHid.java /media/uploads/victorix/usbhid.java

package hid;

/*
 *  Use this class to communicate with a usb hid device using windows 7
 *   
 *   You need:
 *     jna.jar
 *     hidapi.dll 
 *     
 *   Refer to https://github.com/twall/jna for jna.jar
 *   To build hidapi.dll:
 *     download (hidapi-0.7.0.zip) https://github.com/signal11/hidapi/downloads
 *     build it with VisualStudio
 *     
 * References:
 * 
 *   http://www.signal11.us/oss/hidapi/hidapi/doxygen/html/hidapi_8h.html
 *   https://github.com/twall/jna
 *   http://jna.java.net/javadoc/overview-summary.html#wide-strings
 *   
 * @name UsbHid
 * @author victorix - 04.02.2013
 * @version 1 
*/
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.Structure.ByReference;
import com.sun.jna.WString;

/**
 *
 * @author Victorix
 */
public class UsbHid{
  static  String dllFich;
  public static int WSTR_LEN=512;
  static final String DEVICE_NULL="Device null";
  static final int DEVICE_ERROR=-2;
  Dll dll;
  
  public UsbHid(String f){
    dllFich=f;
    dll=Dll.INSTANCE;
  }
  
  public void hidInit(){
    dll.hid_init();
  }
    
  public void hidExit(){
    dll.hid_exit();
  }
    
  String hidGetManufacturer(HidDevice device){
    if(device==null) return DEVICE_NULL;
    MatrixByte wStr = new MatrixByte(WSTR_LEN);
    int res = dll.hid_get_manufacturer_string(device.ptr(), wStr, WSTR_LEN);
    return wStr.toString();
  }
    
  String hidGetProductId(HidDevice device){
    if(device==null) return DEVICE_NULL;
    MatrixByte wStr = new MatrixByte(WSTR_LEN);
    int res = dll.hid_get_product_string(device.ptr(), wStr, WSTR_LEN);
    return wStr.toString();
  }
            
  String hidGetSerialNumber(HidDevice device){ 
    if(device==null) return DEVICE_NULL;
    MatrixByte wStr = new MatrixByte(WSTR_LEN);
    int res = dll.hid_get_serial_number_string(device.ptr(), wStr, WSTR_LEN);
    return wStr.toString();
  }

  public HidDevice hidOpen(int vendor, int product, String serial_number){
    Pointer p = dll.hid_open((short)vendor, (short)product, serial_number==null ? null : new WString(serial_number));
    return (p==null ? null : new HidDevice(p)); 
  }
 
  public HidDevice hidOpenPath(String path){
    Pointer p = dll.hid_open_path(path);
    return (p==null ? null : new HidDevice(p)); 
  }
 
  public void hidClose(HidDevice device){
    if(device!=null)dll.hid_close(device.ptr());
  }    

  public String hidError(HidDevice device){
    if(device==null) return DEVICE_NULL;
    Pointer p=dll.hid_error(device.ptr());
    return p==null ? null :new MatrixByte(p.getByteArray(0, WSTR_LEN)).toString();
  }
    
  public boolean hidNonBlocking(HidDevice device, boolean lock){
    if(device==null) return false;
  	return 0==dll.hid_set_nonblocking(device.ptr(), lock?1:0);
  }

  int hidRead(HidDevice device, byte[] bytes){
    if(device==null || bytes==null) return DEVICE_ERROR;
  	MatrixByte m=new MatrixByte(bytes);
    int res=dll.hid_read(device.ptr(), m, m.matrix.length);
    return res;
  }

  int hidRead(HidDevice device, byte[] bytes, int tmout){
    if(device==null || bytes==null) return DEVICE_ERROR;
    MatrixByte m=new MatrixByte(bytes);
    int res=dll.hid_read_timeout(device.ptr(), m, bytes.length, tmout);
    return res;
  }    

  int hidGetFeatureReport(HidDevice device, byte[] bytes, byte reportId){
    if(device==null || bytes==null) return DEVICE_ERROR;
    MatrixByte m=new MatrixByte(WSTR_LEN); //bytes.length+1);
    m.matrix[0]=reportId;
    int res=dll.hid_get_feature_report(device.ptr(), m, bytes.length+1);
    if(res==-1) return res;
    System.arraycopy(m.matrix, 1, bytes, 0, res);
    return res;    	
  }

  int hidSendFeatureReport(HidDevice device, byte[] bytes, byte reportId){
    if(device==null || bytes==null) return DEVICE_ERROR;
    MatrixByte m=new MatrixByte(bytes.length+1);
    m.matrix[0]=reportId;
    System.arraycopy(bytes, 0, m.matrix, 1, bytes.length);
    int res=dll.hid_get_feature_report(device.ptr(), m, m.matrix.length);
    return res;    	
  }

  String hidGetIndexedString(HidDevice device, int idx){
    if(device==null) return DEVICE_NULL;
    MatrixByte wStr = new MatrixByte(WSTR_LEN);
    int res=dll.hid_get_indexed_string(device.ptr(), idx, wStr, WSTR_LEN);
    return res==-1 ? null: wStr.toString();    	
  }        

  int hidWrite(HidDevice device, byte[] bytes, int len, byte reportId){
    if(device==null || bytes==null) return DEVICE_ERROR;
    MatrixByte m=new MatrixByte(len+1);
    m.matrix[0]=reportId;
    if(bytes.length<len) len=bytes.length;
    if(len>1) System.arraycopy(bytes, 0, m.matrix, 1, len);
    return dll.hid_write(device.ptr(), m, m.matrix.length);
  }

  public HidDeviceInfo hidEnumerate(int vendor, int product){
    HidDeviceInfo p = dll.hid_enumerate((short)vendor, (short)product);
    return p;
  }    
    
  public void hidFreeEnumeration(HidDeviceInfo list){
    dll.hid_free_enumeration(list.getPointer());
  }
    
  /**************************************/

  public static class MatrixByte extends Structure implements ByReference { 
    public byte [] matrix=null;
        
    MatrixByte(int len){matrix = new byte[len]; }
    MatrixByte(byte[] bytes) {matrix=bytes;}    	

	/* wchars are written l i k e   t h i s (with '\0' in between) */
    public String toString(){
      String str="";
      for(int i=0; i<matrix.length && matrix[i]!=0; i+=2)
    	  str+= (char)(matrix[i] | matrix[i+1]<<8);
      return str;
    }      
  }

/****************************************************/
  public static class HidDevice extends Structure implements ByReference {
    public Pointer ptr;
    	
    public HidDevice(Pointer p){ ptr=p; }
    public Pointer ptr() {return ptr;}
  }
      
  public static class HidDeviceInfo extends Structure implements ByReference{
    public String path;
    public short vendor_id;
    public short product_id;
    public WString serial_number;
    public short release_number;
    public WString manufacturer_string;
    public WString product_string;       // Usage Page for this Device/Interface    	(Windows/Mac only).
    public short usage_page;             // Usage for this Device/Interface    	(Windows/Mac only).
    public short usage;   
    public int interface_number;
    public HidDeviceInfo next;           //public HidDeviceInfo.ByReference next;
        
    public HidDeviceInfo next(){return next;}     
        
    public boolean hasNext() { return next!=null;}
    
    public String show(){
      HidDeviceInfo u=this;
      String str="HidDeviceInfo\n";
      str+="\tpath<"+u.path+">\n";
      str+="\tvendor_id "+Integer.toHexString(u.vendor_id)+"\n";
      str+="\tproduct_id "+Integer.toHexString(u.product_id)+"\n";
      str+="\tserial_number<"+u.serial_number+">\n";
      str+="\trelease_number "+u.release_number+"\n";
      str+="\tmanufacturer_string<"+u.manufacturer_string+">\n";
      str+="\tproduct_string<"+u.product_string+">\n";         	
      str+="\tusage_page "+u.usage_page+"\n";
      str+="\tusage "+u.usage+"\n";
      str+="\tinterface_number "+u.interface_number+"\n";         	       	       	
      return str;
    }
  }    
    /***********************************************/    

  public interface Dll extends Library {

    Dll INSTANCE = (Dll) Native.loadLibrary(
            (Platform.isWindows() ? dllFich : "c"), Dll.class);

    void hid_init();
    void hid_exit(); 	
    Pointer hid_open(short vendor_id, short product_id, WString serial_number);
 	void hid_close(Pointer device);
    Pointer hid_error(Pointer device);
    int hid_read(Pointer device, MatrixByte.ByReference bytes, int length);
    int hid_read_timeout(Pointer device, MatrixByte.ByReference bytes, int length, int timeout);
    int hid_write(Pointer device, MatrixByte.ByReference data, int len); 	
    int hid_get_feature_report(Pointer device, MatrixByte.ByReference data, int length);
    int hid_send_feature_report(Pointer device, MatrixByte.ByReference data, int length);
    int hid_get_indexed_string(Pointer device, int idx, MatrixByte.ByReference string, int len);    	
    int hid_get_manufacturer_string(Pointer device, MatrixByte.ByReference str, int len);  
    int hid_get_product_string(Pointer device, MatrixByte.ByReference str, int len);
    int hid_get_serial_number_string(Pointer device, MatrixByte.ByReference str, int len);
    int hid_set_nonblocking (Pointer device, int nonblock);
    HidDeviceInfo hid_enumerate(short vendor_id, short product_id);
    void  hid_free_enumeration (Pointer devs);
    Pointer hid_open_path (String path);
  }
}

Java program to test with the above Mbed test case code

Download this file UsbHidTestCase.java /media/uploads/victorix/usbhidtestcase.java

package hid;

import hid.UsbHid.HidDevice;
import hid.UsbHid.HidDeviceInfo;

/*
 * Use this program to test with the TestCaseCode in the Mbed
 *  code in  http://mbed.org/cookbook/USBHID-bindings-
 * 
 * Victorix 2013.02.04
*/

public class UsbHidTestCase {

	static final int HID_LEN = 8;

	  public static void main(String[] args) {
	    UsbHid hid;
	    
	    // Initialize 
	    hid = new UsbHid("hidapi");
	    hid.hidInit();
	    
	 // Get and Show all hid devices if any
	    HidDeviceInfo info=hid.hidEnumerate(0, 0);
		if(info!=null){
		  HidDeviceInfo h=info;
		  do{
		    System.out.println(h.show());
		    h=h.next();
		  }while (h!=null);
		  hid.hidFreeEnumeration(info); //dispose of the device list
		}
		
	    // Open a device
		HidDevice dev= hid.hidOpen(0x1234, 0x0006, null);
		System.out.println("\nhid.hidOpen() -> " + (dev==null ? "Error" : "Success"));

		byte data[] = new byte[HID_LEN];
		int val;
		// Read - blocking
		hid.hidNonBlocking(dev, false);
		val=hid.hidRead(dev, data); //wait for data
		System.out.print("\nhid.hidRead() ->\t<" + val+"> <Error: "  + hid.hidError(dev)+"> <"  +showData(data, val)+">\n");

	    // Write
	    String message="Works!:)";
	    val=hid.hidWrite(dev, message.getBytes(), HID_LEN, (byte) 0);
	    if(val!=-1) System.out.print("\nhid.hidWrite() ->\t<" + val+"> <Error: "  + hid.hidError(dev)+">\n");

	  }
	  
	  static String showData(byte b[], int val){
		    String str="";
		    for(int i=0; i<b.length && i<val; i++) str+= " "+Integer.toHexString(b[i] & 0x0FF);
		    return str;
		  } 
}

Conclusion

An example using python has been developed to communicate with the mbed but it would be great to develop a lot of programs in C, C++, Java, Haskell, Scala, Factor, ... having the same capabilities.

Feel free to contribute to this webpage by developing programs able to communicate with your mbed. You can use your own protocol on top of the USBHID layer to design your own USB device!