Hello guys!
Recently I have been working on a project that requires HID Touchscreen functions (compatible with Android) and I have been figuring out how to port the DigiCombo.h, a library which is capable of this but is designed to work on Digispark only, to one that can be used for Leonardo or Micro. Since it's unrealistic to redo the library work on my own, I just picked out the core part of it and tested it on Leonardo when I encountered a lot of questions. It seems that my code and Leonardo can't work properly with Android system while using the original library on Digispark is near to perfect in any situation.
Note in advance: this project may require proficiency and experience in HID, USB, multi-touch Touchscreen descriptor, and something else relevant. Also you need to have a glimpse into DigiCombo lib files (available in github) and my explanations are written in the comment sections of my code. Also(again:), it's hard to test the invisible multi-touch in Android, thus I test it with APDE (Processing for android), and the code will be given at the end.
Here is my code on Leonardo:
#include <HID.h>
#define REPORT_ID_TOUCH 0x01
#define CONTACT_COUNT_MAXIMUM 10
// copied from DigiComboC.c, maybe it isn't the problem
static const uint8_t _hidReportDescriptorTouch[] PROGMEM = {
0x05, 0x0D, // USAGE_PAGE(Digitizers)
0x09, 0x04, // USAGE (Touch Screen)
0xA1, 0x01, // COLLECTION(Application)
0x85, REPORT_ID_TOUCH, // REPORT_ID (Touch)
0x09, 0x55, // USAGE (Contact Count Maximum)
0x25, CONTACT_COUNT_MAXIMUM, // LOGICAL_MAXIMUM (CONTACT_COUNT_MAXIMUM)
0xB1, 0x02, // FEATURE (Data,Var,Abs)
0x09, 0x54, // USAGE (Contact count)
0x95, 0x01, // REPORT_COUNT(1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x22, // USAGE (Finger)
0xA1, 0x02, // COLLECTION (Logical)
0x09, 0x51, // USAGE (Contact Identifier)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x42, // USAGE (Tip Switch)
0x09, 0x32, // USAGE (In Range)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x02, // REPORT_COUNT(2)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x81, 0x03, // INPUT (Cnst,Ary,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x16, 0x00, 0x00, // Logical Minimum (0)
0x26, 0x10, 0x27, // Logical Maximum (10000)
0x36, 0x00, 0x00, // Physical Minimum (0)
0x46, 0x10, 0x27, // Physical Maximum (10000)
0x66, 0x00, 0x00, // UNIT (None)
0x75, 0x10, // Report Size (16),
0x95, 0x02, // Report Count (2),
0x81, 0x02, // Input (Data,Var,Abs)
0xC0, // END_COLLECTION
0xC0, // END_COLLECTION
// with this declaration a data packet must be sent as:
// byte 1 -> "contact count" (always == 1) <===== Please note this! So weird! But it actually work in this library.
// byte 2 -> "contact identifier" (any value)
// byte 3 -> "Tip Switch" state (bit 0 = Tip Switch up/down, bit 1 = In Range) <===== no idea what it means
// byte 4,5 -> absolute X coordinate (0...10000)
// byte 6,7 -> absolute Y coordinate (0...10000)
};
// initialization
void begin(void)
{
// register as an HID device
static HIDSubDescriptor node(_hidReportDescriptorTouch, sizeof(_hidReportDescriptorTouch));
HID().AppendDescriptor(&node);
}
// stimulate a finger <===== similar to DigiCombo's function: moveFinger(int, int, int)
void _move(uint8_t count, uint8_t contact_identifier, uint8_t state, uint16_t x, uint16_t y)
{
uint8_t buffer [7];
buffer[0] = count; // This byte is the so-called Contact Count
buffer[1] = contact_identifier & 0xFF; // "contact identifier"
buffer[2] = state & 0xFF; //(bit 0 = Tip Switch up/down, bit 1 = In Range) <===== still no idea :(
buffer[3] = x & 0xFF; // low byte x_coord
buffer[4] = (x >> 8) & 0xFF; // high byte x_coord
buffer[5] = y & 0xFF;// low byte y_coord
buffer[6] = (y >> 8) & 0xFF; // high byte y_coord <===== send the coordinate
HID().SendReport(REPORT_ID_TOUCH, buffer, 7);
}
void setup() {
begin();
delay(1000); // seems that we must add a delay of 1000ms just as Digi_Combo::begin() in DigiCombo.cpp. Otherwise, we would miss the first Contact
}
void loop() {
// Just testing
_move(1, 0x01, 0b11, 2000, 1000);
delay(1000);
_move(1, 0x02, 0b11, 1000, 2000);
delay(1000);
_move(1, 0x03, 0b11, 1000, 1000);
delay(1000);
_move(1, 0x04, 0b11, 2000, 2000);
delay(1000);
// Things go well above. Problems come as follows when I release the touches.
_move(4, 0x04, 0b00, 0, 0);
delay(1000);
// It can't cancel the touch(id=4)
_move(3, 0x03, 0b00, 0, 0);
delay(1000);
// It can't cancel the touch(id=3) either
_move(2, 0x02, 0b00, 0, 0);
delay(1000);
// It cancel both touch(id=1) and touch(id=4) at a time
_move(1, 0x01, 0b00, 0, 0);
delay(1000);
// It cancel both touch(id=2) and touch(id=3)
delay(1000);
}
As you can see, it's extremely weird, confusing and I have no idea what causes the problem. My code is almost copied from the library files, and it should be free from errors.
Now I have to paste some related library code for reference.
// ......
void Digi_Combo::moveFinger(uint16_t x, uint16_t y, uint8_t contactId)
{
uint8_t id = contactId != 0xFF ? contactId : lastContactId;
contacts[id] = 1;
report_buffer[0] = REPORT_ID_TOUCH;
report_buffer[1] = 1;
report_buffer[2] = id;
report_buffer[3] = 0b11;
report_buffer[4] = MSB(x);
report_buffer[5] = LSB(x);
report_buffer[6] = MSB(y);
report_buffer[7] = LSB(y);
usbReportSend(REPORT_SIZE_TOUCH);
}
// ......
void Digi_Combo::release(uint8_t contactId)
{
uint8_t id = contactId != 0xFF ? contactId : lastContactId;
uint8_t currentlyCountContact = countContact();
if (lastReportId == REPORT_ID_TOUCH) contacts[id] = 0;
report_buffer[0] = lastReportId;
report_buffer[1] = lastReportId == REPORT_ID_TOUCH ? currentlyCountContact : 0;
report_buffer[2] = lastReportId == REPORT_ID_TOUCH ? id : 0;
report_buffer[3] = 0;
report_buffer[4] = 0;
report_buffer[5] = 0;
report_buffer[6] = 0;
report_buffer[7] = 0;
usbReportSend(BUFFER_SIZE);
}
For more info, you need to fetch DigiCombo.cpp, DigiCombo.h, DigiComboC.c, DigiComboC.h. I'm sorry about that.
Here is my APDE code for multi-touch testing:
SEE=====> APDE Code For Multi-touch Testing
Thanks in advance