USB I/O API

From SpinetiX Support Wiki

Jump to: navigation, search

Introduction

The USB I/O API allows interacting with USB devices that comply to the HID specification, directly from the JavaScript code. It was experimentally introduced within firmware 3.0.0 (enabled by default) and stabilized within firmware 3.1.0 (disabled by default - to enable it, go to HMP Control Center > I/O Automation > USB Settings).

There are many devices that use HID for more than just keyboards, mice and touch screens - for instance general purpose I/O devices, thermometers, relays, sensors, etc. This API supports enumeration as well as sending and receiving input, output and feature reports. It is suggested that you are somewhat familiar with the USB HID class definition and HID usage tables to effectively use this API.

  • Note that USB HID devices such as keyboards, mice and touch screens are not reported via this API but via regular SVG input events.

When an HID device is plugged into or removed from the HMP USB port, an HID_DEVICE_PLUGGED or HID_DEVICE_REMOVED hid event is generated (see below). Hot-plugging is supported only on the HMP and it is not supported on Elementi.

Enumerating available USB devices

The HID global variable is an array of HumanInterfaceDevice objects representing the interfaces exposed by all connected USB HID devices. Note that multifunction devices are allowed to expose more then one I/O interface per physical device; these will have different "usage" codes.

 interface HumanInterfaceDevice {
   attribute unsigned short deviceID;
   attribute unsigned short vendorID;
   attribute unsigned short productID;
   attribute unsigned short revisionID;
   attribute DOMString vendorName;
   attribute DOMString productName;
   attribute DOMString serialNumber;
   attribute unsigned short usagePage;
   attribute unsigned short usage;
   attribute Array inputReports;
   attribute Array outputReports;
   attribute Array featureReports;

   sendOutputReport( [in unsigned short reportID,] in Array values );
   sendFeatureReport( [in unsigned short reportID,] in Array values );
   getFeatureReport( in unsigned short reportID );
 };
  • deviceID is a number used to identify the device in the system, it is attributed during enumeration.
  • vendorID, productID, revisionID, vendorName, productName and serialNumber are provided by the USB device to identify the hardware. They are shared by all functions of a multi-function device.
  • usagePage and usage identify the functionality of an I/O interface according to USB HID usage tables.
  • The inputReports, outputReports and featureReports are arrays of HIDReportFields objects that enumerate the available reports for this device by category.

HID Reports description

 interface HIDReportFields : Array of HIDReportField {
   attribute unsigned short reportID;
 };

reportID is the unique identifier for this report, as defined in the HID report descriptor. The reserved value 0 is used for devices with a single report which do not define a report ID in the descriptor.

Each field of each main item in the HID report descriptor is described by a HIDReportField object, the attributes of which define the function of the field according to USB HID usage tables. All fields of all main items are described in a flat array.

 interface HIDReportField {
   attribute short index;
   attribute unsigned short usagePage;
   attribute unsigned short usage;
   attribute unsigned short collectionUsagePage;
   attribute unsigned short collectionUsage;
 };

index represents the assigned position of the field and uniquely identifies it within the report, the same indices are used to report or set field values when receiving and sending reports. Note, however, that the assignment of indices is not the same in Elementi and HMP due to limitations in the Windows HID API.

The collectionUsagePage and collectionUsage provide the application collection within which the main item is defined.

Examples

Example: Show all USB devices

var listOfDevices = HID;
for ( var i = 0; i < listOfDevices.length; i++ ) {
  var device = listOfDevices[i];
  alert( 'Found: ' + device.vendorID.toString(16) + ':' + device.productID.toString(16) + '(' + device.vendorName + ' ' + device.productName + ')' );
}

In most cases you will use the HID array to find the device you want to control by comparing the vendorID and productID attributes.

Sending output reports

To set an output on the device you need to send an output report to it.

Output reports contain a group of values that are changed together. It is possible to provide a new value for each field in the report or only for some, the other one being unchanged.

  • To set all fields of the report at once, call sendOutputReport with an javascript array containing all the values.
  • To set selected fields, call sendOutputReport with a javascript object with only the selected fields defined.

Examples

In the example below we assume our device has a single output report consisting of 6 fields with 8-bit values:

// our device is 0x1234:0x4567
var dev = null;
for ( var i = 0; i < HID.length; i++ ) {
  if ( HID[i].vendorID==0x1234 and HID[i].productID==0x4567 ) {
    dev = HID[i];
    break;
  }
}

if ( dev ) {
  dev.sendOutputReport( [ 0xBA, 0xDC, 0xAF, 0xFE, 0xFE, 0xED ] );  // Initialize all output values with one report
  dev.sendOutputReport( { 0: 0xFA, 5: 0xCE } );   // Change only the first and the last value
}

Receiving input reports

To receive inputs from USB devices, add a listener for the "hid" event on the window object.

The event handler gets called whenever an incoming report is received and is passed in a HIDEvent argument. The same handler also gets called when a USB device is inserted or removed and when feature reports are received. Use the action property to distinguish between these events.

 interface HIDEvent : Event {
   unsigned short HID_DEVICE_PLUGGED = 0;
   unsigned short HID_DEVICE_REMOVED = 1;
   unsigned short HID_REPORT_IN      = 2;
   unsigned short HID_REPORT_FEATURE = 3;   

   attribute unsigned short action;
   attribute HumanInterfaceDevice device;
   attribute HIDReport report;
 }

The HIDEvent object contains these attributes in additiong to those common to all Event objects:

  • action
    One of the four possible action codes: HID_DEVICE_PLUGGED, HID_DEVICE_REMOVED, HID_REPORT_IN or HID_REPORT_FEATURE.
  • device
    HumanInterfaceDevice object describing the device that sent a report.
  • report
    HIDReport object, only present for report in and report feature events.

HID Report fields

 interface HIDReport : Array of long {
   unsigned short reportID;

   long getUsage( unsigned short usagePage, unsigned short usage );
 }

The HIDReport is an array of integer values for each field in the report, with two additions:

  • reportID
    Identifies the type of report
  • getUsage
    The function allows getting a field from the report by looking up its usage code rather than directly using the field index.

Examples

In the example below, we assume the same device using in the preceding example will also send input reports with 6 values, but we are interested in only the first one and the last one:

// our device is 0x1234:0x4567
var dev = null;
for ( var i = 0; i < HID.length; i++ ) {
  if ( HID[i].vendorID==0x1234 and HID[i].productID==0x4567 ) {
    dev = HID[i];
    break;
  }
}

window.addEventListener( "hid", function( ev ) {
  if ( ev.action == HIDEvent.HID_REPORT_IN && ev.device == dev ) {
    for (var n = 0; n < ev.report.length; n++) {
      if (ev.report[n] !== null) {
        alert('Got ev.report[' + n + '] = ' + ev.report[n]);
      }
    }
  }
});

Feature reports

Feature reports are seldom used with USB HID devices, nevertherless it is possible to send a feature report with the sendFeatureReport method, which works exactly like sendOutputReport.

To receive a feature report you must use the getFeatureReport method, which will ask the device to send a feature report and will trigger an hid event when the report is eventually received.

This page was last modified on 27 February 2015, at 13:12.