eshalev
eshalev

Reputation: 3265

USB HID multitouch serial reporting

I am creating an Arduino device that connects to a Windows computer and simulates a touchscreen on the computer. The screen does not belong to the Arduino, but rather the Arduino relays HID touch messages to the Windows 10 computer via USB.

The first thing I did was to relay AbsoluteMouse events instead of Digitizer events. This worked fine for me. However, as my device supports multiple touches, I would like to use multi-touch HID messages instead of AbsoluteMouse. As a proof of concept, I am starting with relaying only one finger's data. (I plan to add more fingers after I get this to work). I am taking the SerialReport approach, as it seems to be the easiest to implement:

I have found many examples of using AbsoluteMouse, so basically I didn't have to do much more than copy-paste the HID reports. However, with touchscreens it is very hard to come by code samples. (I have only found the synaptic touchpad sample which is not a touchscreen). As such I have tried to implement my own code to report touch messages. But this fails. I think I understand how to interpret the HID descriptor, but this is my first time, so I am not sure... Is there anything wrong with my message structure?

#define REPORTID_MTOUCH                 1

static const uint8_t _hidSerialMultiTouchDescriptor[] PROGMEM = {
    // https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/sample-report-descriptor--serial-reporting-mode-
    // Linked from: https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/selecting-packet-reporting-modes-in-multitouch-drivers
    0x05, 0x0d,                         // USAGE_PAGE (Digitizers)
    0x09, 0x04,                         // USAGE (Touch Screen)
    0xa1, 0x01,                         // COLLECTION (Application)
    0x85, REPORTID_MTOUCH,              //   REPORT_ID (Touch)
    0x09, 0x22,                         //   USAGE (Finger)
    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 Desk..
    0x26, 0xff, 0x7f,                   //     LOGICAL_MAXIMUM (32767)
    0x75, 0x10,                         //     REPORT_SIZE (16)
    0x95, 0x01,                         //     REPORT_COUNT (1)
    0x65, 0x00,                         //     UNIT (None)
    0x09, 0x30,                         //     USAGE (X)
    0x81, 0x02,                         //     INPUT (Data,Var,Abs)
    0x09, 0x31,                         //     USAGE (Y)
    0x46, 0x00, 0x00,                   //     PHYSICAL_MAXIMUM (0)
    0x81, 0x02,                         //     INPUT (Data,Var,Abs)
    0x05, 0x0d,                         //     USAGE PAGE (Digitizers)
    0x09, 0x48,                         //     USAGE (Width)
    0x09, 0x49,                         //     USAGE (Height)
    0x95, 0x01,                         //     REPORT_COUNT (2)
    0x81, 0x02,                         //     INPUT (Data,Var,Abs)
    0x81, 0x03,                         //     INPUT (Cnst,Ary,Abs)
    0x09, 0x51,                         //     USAGE (Contact Identifier)
    0x75, 0x10,                         //     REPORT_SIZE (16)
    0x95, 0x02,                         //     REPORT_COUNT (1)
    0x81, 0x02,                         //     INPUT (Data,Var,Abs)
    0x09, 0x55,                         //    USAGE(Maximum Count)
    0x15, 0x00,                         //   LOGICAL_MINIMUM (0)
    0x25, 0x08,                         //   LOGICAL_MAXIMUM (255)
    0x75, 0x08,                         //   REPORT_SIZE (8)
    0x95, 0x01,                         //   REPORT_COUNT (1)
    0xb1, 0x02,                         //   FEATURE (Data,Var,Abs)
    0xc0,                               //   END_COLLECTION
    0xc0                               // END_COLLECTION
};

This is my implementation of said message:

typedef union {
  struct{
    uint8_t TipSwitchLsb4Bits_InRange_Confidence_Empty2Bits;
    uint8_t Padding8_10Minus2;
    int16_t xAxis;
    int16_t yAxis;
    int16_t width;
    int16_t height;
    int16_t ContactId;
    uint8_t MaximumCount;
  };
} HID_SerialTouchReport;

void reportTouch(unsigned int x, unsigned int y) {
  HID_SerialTouchReport report;
  report.TipSwitchLsb4Bits_InRange_Confidence_Empty2Bits = 0xff;
  report.xAxis = x;
  report.yAxis = y;
  report.width = 4;
  report.height = 4;
  report.ContactId = 0;
  report.MaximumCount = 1;
  HID().SendReport(REPORTID_MTOUCH, &report, sizeof(report));
}

...
  static HIDSubDescriptor node(_hidSerialMultiTouchDescriptor, sizeof(_hidSerialMultiTouchDescriptor));
HID().AppendDescriptor(&node);
...

//Move the pointer diagonally across the screen:
for(unsigned int x=0; x<32767; x+=1000) {
  reportTouch(x, x);
  delay(50);
}

The test code is supposed to draw a diagonal line from top left to bottom right on Microsoft Paint.

When I test this with an AbsoluteMouse descriptor/Message everything works. So I presume the problem is in my definition of HID_SerialTouchReport.

Some things that have grabbed my attention:

  1. Why does "PHYSICAL_MAXIMUM (0)" come while still defining "USAGE (Y)". Is it somehow relevant to Y and not X? And why is it "0"?
  2. What is the significance of "Ary" in "INPUT (Cnst, Ary, Abs)" for height and width?
  3. I am sort of "fudging" things with: "report.TipSwitchLsb4Bits_InRange_Confidence_Empty2Bits=0xff;". However, this seems alright to me, because I think all the non-padded bits need to be set to "1" anyway.
  4. I am not sure what I should set report.MaximumCount to be. I have tried experimenting with different Count & Id values, but all had no effect.
  5. My Arduino does show up as a "composite device" in Device Manager, so I figure not all is wrong.
  6. In the article I linked "Serial Packet" mode is for Legacy Windows 7, and I am using Windows 10. But I figure this should not be a problem.
  7. While I did copy the HID descriptor directly from the Microsoft site, I am not sure I properly constructed the message structure. (Did I forget a few bits?)

Or is it something entirely other which is causing my code to fail?

Upvotes: 3

Views: 3638

Answers (1)

aja
aja

Reputation: 1720

It's hard to say for sure but it looks like the HID report descriptor does not match your HID_SerialTouchReport structure. The following is how I decoded your descriptor:

//--------------------------------------------------------------------------------
// Decoded Application Collection
//--------------------------------------------------------------------------------

/*
05 0D        (GLOBAL) USAGE_PAGE         0x000D Digitizer Device Page 
09 04        (LOCAL)  USAGE              0x000D0004 Touch Screen (CA=Application Collection) 
A1 01        (MAIN)   COLLECTION         0x00000001 Application (Usage=0x000D0004: Page=Digitizer Device Page, Usage=Touch Screen, Type=CA)
85 01          (GLOBAL) REPORT_ID          0x01 (1) 
09 22          (LOCAL)  USAGE              0x000D0022 Finger (CL=Logical Collection) 
A1 00          (MAIN)   COLLECTION         0x00000000 Physical (Usage=0x000D0022: Page=Digitizer Device Page, Usage=Finger, Type=CL) <-- Warning: USAGE type should be CP (Physical)
09 42            (LOCAL)  USAGE              0x000D0042 Tip Switch (MC=Momentary Control) 
15 00            (GLOBAL) LOGICAL_MINIMUM    0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
25 01            (GLOBAL) LOGICAL_MAXIMUM    0x01 (1)  
75 01            (GLOBAL) REPORT_SIZE        0x01 (1) Number of bits per field  
95 01            (GLOBAL) REPORT_COUNT       0x01 (1) Number of fields  
81 02            (MAIN)   INPUT              0x00000002 (1 field x 1 bit) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
95 03            (GLOBAL) REPORT_COUNT       0x03 (3) Number of fields  
81 03            (MAIN)   INPUT              0x00000003 (3 fields x 1 bit) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
09 32            (LOCAL)  USAGE              0x000D0032 In Range (MC=Momentary Control) 
09 47            (LOCAL)  USAGE              0x000D0047 Confidence (DV=Dynamic Value) 
95 02            (GLOBAL) REPORT_COUNT       0x02 (2) Number of fields  
81 02            (MAIN)   INPUT              0x00000002 (2 fields x 1 bit) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
95 0A            (GLOBAL) REPORT_COUNT       0x0A (10) Number of fields  
81 03            (MAIN)   INPUT              0x00000003 (10 fields x 1 bit) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
05 01            (GLOBAL) USAGE_PAGE         0x0001 Generic Desktop Page 
26 FF7F          (GLOBAL) LOGICAL_MAXIMUM    0x7FFF (32767)  
75 10            (GLOBAL) REPORT_SIZE        0x10 (16) Number of bits per field  
95 01            (GLOBAL) REPORT_COUNT       0x01 (1) Number of fields  
65 00            (GLOBAL) UNIT               0x00000000 No unit (0=None) <-- Redundant: UNIT is already 0x00000000
09 30            (LOCAL)  USAGE              0x00010030 X (DV=Dynamic Value) 
81 02            (MAIN)   INPUT              0x00000002 (1 field x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
09 31            (LOCAL)  USAGE              0x00010031 Y (DV=Dynamic Value) 
46 0000          (GLOBAL) PHYSICAL_MAXIMUM   0x0000 (0) <-- Redundant: PHYSICAL_MAXIMUM is already 0 <-- Info: Consider replacing 46 0000 with 44
81 02            (MAIN)   INPUT              0x00000002 (1 field x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
05 0D            (GLOBAL) USAGE_PAGE         0x000D Digitizer Device Page 
09 48            (LOCAL)  USAGE              0x000D0048 Width (DV=Dynamic Value) 
09 49            (LOCAL)  USAGE              0x000D0049 Height (DV=Dynamic Value) 
95 01            (GLOBAL) REPORT_COUNT       0x01 (1) Number of fields <-- Redundant: REPORT_COUNT is already 1 
81 02            (MAIN)   INPUT              0x00000002 (1 field x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
81 03            (MAIN)   INPUT              0x00000003 (1 field x 16 bits) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
09 51            (LOCAL)  USAGE              0x000D0051 Contact Identifier (DV=Dynamic Value) 
75 10            (GLOBAL) REPORT_SIZE        0x10 (16) Number of bits per field <-- Redundant: REPORT_SIZE is already 16 
95 02            (GLOBAL) REPORT_COUNT       0x02 (2) Number of fields  
81 02            (MAIN)   INPUT              0x00000002 (2 fields x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
09 55            (LOCAL)  USAGE              0x000D0055 Contact Count Maximum (SV=Static Value) 
15 00            (GLOBAL) LOGICAL_MINIMUM    0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
25 08            (GLOBAL) LOGICAL_MAXIMUM    0x08 (8)  
75 08            (GLOBAL) REPORT_SIZE        0x08 (8) Number of bits per field  
95 01            (GLOBAL) REPORT_COUNT       0x01 (1) Number of fields  
B1 02            (MAIN)   FEATURE            0x00000002 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
C0             (MAIN)   END_COLLECTION     Physical 
C0           (MAIN)   END_COLLECTION     Application 
*/

//--------------------------------------------------------------------------------
// Digitizer Device Page featureReport 01 (Device <-> Host)
//--------------------------------------------------------------------------------

typedef struct
{
  uint8_t  reportId;                                 // Report ID = 0x01 (1)
                                                     // Collection: TouchScreen Finger
  uint8_t  DIG_TouchScreenFingerContactCountMaximum; // Usage 0x000D0055: Contact Count Maximum, Value = 0 to 8
} featureReport01_t;


//--------------------------------------------------------------------------------
// Digitizer Device Page inputReport 01 (Device --> Host)
//--------------------------------------------------------------------------------

typedef struct
{
  uint8_t  reportId;                                 // Report ID = 0x01 (1)
                                                     // Collection: TouchScreen Finger
  uint8_t  DIG_TouchScreenFingerTipSwitch : 1;       // Usage 0x000D0042: Tip Switch, Value = 0 to 1
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  DIG_TouchScreenFingerInRange : 1;         // Usage 0x000D0032: In Range, Value = 0 to 1
  uint8_t  DIG_TouchScreenFingerConfidence : 1;      // Usage 0x000D0047: Confidence, Value = 0 to 1
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint16_t GD_TouchScreenFingerX;                    // Usage 0x00010030: X, Value = 0 to 32767
  uint16_t GD_TouchScreenFingerY;                    // Usage 0x00010031: Y, Value = 0 to 32767
  uint16_t DIG_TouchScreenFingerWidth;               // Usage 0x000D0048: Width, Value = 0 to 32767
                                                     // Usage 0x000D0049 Height (DV=Dynamic Value) Value = 0 to 32767 <-- Ignored: REPORT_COUNT (1) is too small
  uint16_t pad_8;                                    // Pad
  uint16_t DIG_TouchScreenFingerContactIdentifier[2]; // Usage 0x000D0051: Contact Identifier, Value = 0 to 32767
} inputReport01_t;

When I set the correct report count for Width and Height and remove the (unnecessary?) pad byte, the unnecessary UNIT and PHYSICAL_MAXIMUM, and redundant GLOBAL items, and change the contact identifier REPORT_COUNT from 2 to 1, it looks better to me:

//--------------------------------------------------------------------------------
// Decoded Application Collection
//--------------------------------------------------------------------------------

/*
05 0D        (GLOBAL) USAGE_PAGE         0x000D Digitizer Device Page 
09 04        (LOCAL)  USAGE              0x000D0004 Touch Screen (CA=Application Collection) 
A1 01        (MAIN)   COLLECTION         0x00000001 Application (Usage=0x000D0004: Page=Digitizer Device Page, Usage=Touch Screen, Type=CA)
85 01          (GLOBAL) REPORT_ID          0x01 (1) 
09 22          (LOCAL)  USAGE              0x000D0022 Finger (CL=Logical Collection) 
A1 00          (MAIN)   COLLECTION         0x00000000 Physical (Usage=0x000D0022: Page=Digitizer Device Page, Usage=Finger, Type=CL) <-- Warning: USAGE type should be CP (Physical)
09 42            (LOCAL)  USAGE              0x000D0042 Tip Switch (MC=Momentary Control) 
15 00            (GLOBAL) LOGICAL_MINIMUM    0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
25 01            (GLOBAL) LOGICAL_MAXIMUM    0x01 (1)  
75 01            (GLOBAL) REPORT_SIZE        0x01 (1) Number of bits per field  
95 01            (GLOBAL) REPORT_COUNT       0x01 (1) Number of fields  
81 02            (MAIN)   INPUT              0x00000002 (1 field x 1 bit) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
95 03            (GLOBAL) REPORT_COUNT       0x03 (3) Number of fields  
81 03            (MAIN)   INPUT              0x00000003 (3 fields x 1 bit) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
09 32            (LOCAL)  USAGE              0x000D0032 In Range (MC=Momentary Control) 
09 47            (LOCAL)  USAGE              0x000D0047 Confidence (DV=Dynamic Value) 
95 02            (GLOBAL) REPORT_COUNT       0x02 (2) Number of fields  
81 02            (MAIN)   INPUT              0x00000002 (2 fields x 1 bit) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
95 0A            (GLOBAL) REPORT_COUNT       0x0A (10) Number of fields  
81 03            (MAIN)   INPUT              0x00000003 (10 fields x 1 bit) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
05 01            (GLOBAL) USAGE_PAGE         0x0001 Generic Desktop Page 
26 FF7F          (GLOBAL) LOGICAL_MAXIMUM    0x7FFF (32767)  
75 10            (GLOBAL) REPORT_SIZE        0x10 (16) Number of bits per field  
95 01            (GLOBAL) REPORT_COUNT       0x01 (1) Number of fields  
09 30            (LOCAL)  USAGE              0x00010030 X (DV=Dynamic Value) 
81 02            (MAIN)   INPUT              0x00000002 (1 field x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
09 31            (LOCAL)  USAGE              0x00010031 Y (DV=Dynamic Value) 
81 02            (MAIN)   INPUT              0x00000002 (1 field x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
05 0D            (GLOBAL) USAGE_PAGE         0x000D Digitizer Device Page 
09 48            (LOCAL)  USAGE              0x000D0048 Width (DV=Dynamic Value) 
09 49            (LOCAL)  USAGE              0x000D0049 Height (DV=Dynamic Value) 
95 02            (GLOBAL) REPORT_COUNT       0x02 (2) Number of fields  
81 02            (MAIN)   INPUT              0x00000002 (2 fields x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
09 51            (LOCAL)  USAGE              0x000D0051 Contact Identifier (DV=Dynamic Value) 
95 01            (GLOBAL) REPORT_COUNT       0x01 (1) Number of fields  
81 02            (MAIN)   INPUT              0x00000002 (1 field x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
09 55            (LOCAL)  USAGE              0x000D0055 Contact Count Maximum (SV=Static Value) 
25 08            (GLOBAL) LOGICAL_MAXIMUM    0x08 (8)  
75 08            (GLOBAL) REPORT_SIZE        0x08 (8) Number of bits per field  
B1 02            (MAIN)   FEATURE            0x00000002 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
C0             (MAIN)   END_COLLECTION     Physical 
C0           (MAIN)   END_COLLECTION     Application 
*/

//--------------------------------------------------------------------------------
// Digitizer Device Page featureReport 01 (Device <-> Host)
//--------------------------------------------------------------------------------

typedef struct
{
  uint8_t  reportId;                                 // Report ID = 0x01 (1)
                                                     // Collection: TouchScreen Finger
  uint8_t  DIG_TouchScreenFingerContactCountMaximum; // Usage 0x000D0055: Contact Count Maximum, Value = 0 to 8
} featureReport01_t;


//--------------------------------------------------------------------------------
// Digitizer Device Page inputReport 01 (Device --> Host)
//--------------------------------------------------------------------------------

typedef struct
{
  uint8_t  reportId;                                 // Report ID = 0x01 (1)
                                                     // Collection: TouchScreen Finger
  uint8_t  DIG_TouchScreenFingerTipSwitch : 1;       // Usage 0x000D0042: Tip Switch, Value = 0 to 1
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  DIG_TouchScreenFingerInRange : 1;         // Usage 0x000D0032: In Range, Value = 0 to 1
  uint8_t  DIG_TouchScreenFingerConfidence : 1;      // Usage 0x000D0047: Confidence, Value = 0 to 1
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint8_t  : 1;                                      // Pad
  uint16_t GD_TouchScreenFingerX;                    // Usage 0x00010030: X, Value = 0 to 32767
  uint16_t GD_TouchScreenFingerY;                    // Usage 0x00010031: Y, Value = 0 to 32767
  uint16_t DIG_TouchScreenFingerWidth;               // Usage 0x000D0048: Width, Value = 0 to 32767
  uint16_t DIG_TouchScreenFingerHeight;              // Usage 0x000D0049: Height, Value = 0 to 32767
  uint16_t DIG_TouchScreenFingerContactIdentifier;   // Usage 0x000D0051: Contact Identifier, Value = 0 to 32767
} inputReport01_t;

These HID report descriptors are so tricky to handcraft that I'm not surprised that even Microsoft gets it wrong from time to time. If only some programming whiz (i.e way better than me) could come up with some decent software to create them I think the world would be a better place :)

Disclosure: I decoded your report descriptor using some free software I wrote called hidrdd. I've been meaning to port it to Python but haven't got around to learning Python yet.

BTW, the difference between var and ary can be best understood as the difference between C declarations:

// var is like:
uint8_t my_value1;
uint8_t my_value2;

// ary is like:
uint8_t my_value[2];

The maximumcount thing appears to be the "Contact Count Maximum" usage - i.e. the maximum number of fingers pressed concurrently and is part of a feature report (that can be sent or received from the device). The report descriptor indicates that it can have a value of between 0 fingers and 8 fingers concurrently pressed.

Upvotes: 1

Related Questions