T3.6 USB Host - Raw Hid

KurtE

Senior Member+
As I mentioned in the USB Mouse thread (https://forum.pjrc.com/threads/45740-USB-Host-Mouse-Driver?p=159888&viewfull=1#post159888) , I have been playing around now with adding RAW Hide support.

More details about it in the other thread. Support is still in my own fork and branch: https://github.com/KurtE/USBHost_t36/tree/RAWHID2


Question to self and Paul and ... - Wonder if we should have top level threads for the USB Host support for different types with the first message showing the status of the different USB types. Example USB Mouse - Has been tried on X, Y, Z...

It is still a WIP, but thought I would try to do something with it to get an idea of how well it worked (or not).

So I wrote a Quick and dirty app that updates an ILI9341 display (currently using my ili9341_t3n library could use _t3 library). It draws a couple of screens and then waits for data on the RAWHID object. It assumes packet size is 64, the first 4 bytes are Frame number, and packet within frame followed by 30 pixels of data... When it receives the data, it updates the display...
Current program:
Code:
// To use Kurt's Flexiboard uncomment the line below:
#define KURTS_FLEXI

#include <ILI9341_t3n.h>
#ifdef KURTS_FLEXI
#define TFT_DC 22
#define TFT_CS 15
#define TFT_RST -1
#define TFT_SCK 14
#define TFT_MISO 12
#define TFT_MOSI 7
#define DEBUG_PIN 13
#else
#define TFT_DC  9
#define TFT_CS 10
#define TFT_RST 7
#define TFT_SCK 13
#define TFT_MISO 12
#define TFT_MOSI 11
#endif

uint8_t buffer[64]; // Raw hid buffer.
uint16_t *pixel_data = (uint16_t*)(&buffer[4]);
uint16_t frame_number = 0;  // What frame number are we on.
uint16_t frame_packet_number = 0xffff; // Which packet within a frame are we?
const uint16_t packets_per_frame = 320 * 240 * 2 / 60; // We will use 60 data bytes per packet

elapsedMillis msFrameTimeout;
elapsedMillis msFrame;
#define FRAME_TIMEOUT 10

ILI9341_t3n tft = ILI9341_t3n(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCK, TFT_MISO);

// Size of the color selection boxes and the paintbrush size

void setup(void) {
  pinMode(0, OUTPUT);
  digitalWrite(0, LOW);

  while (!Serial && (millis() <= 1000));

  Serial.begin(9600);
  Serial.println(F("RawHID remote display"));

  tft.begin();
  tft.setRotation(1);
  tft.fillScreen(ILI9341_RED);
  delay(250);
  tft.fillScreen(ILI9341_GREEN);
  delay(250);
  tft.fillScreen(ILI9341_BLACK);
  delay(250);
  tft.drawRect(0, 0, tft.width(), tft.height(), ILI9341_BLUE);
  tft.setCursor(10, tft.height() / 2);
  tft.setTextColor(ILI9341_WHITE);
  tft.print("Display Ready!");
  Serial.println("Remote display ready");
}


void loop()
{
  int n = RawHID.recv(buffer, 0); // 0 timeout = do not wait
  if (n > 0) {
    digitalWrite(0, HIGH);
    // Lets extract the frame and packet numbers
    uint16_t buffer_frame_number = buffer[0] + (uint16_t)(buffer[1] << 8);
    uint16_t buffer_frame_packet_number = buffer[2] + (uint16_t)(buffer[3] << 8);
    if (buffer_frame_number != frame_number) {
      frame_number = buffer_frame_number;
      Serial.printf("New frame: %d %d\n", frame_number, buffer_frame_packet_number);
      msFrame = 0;
    } else {
      if (buffer_frame_packet_number != (frame_packet_number + 1)) {
        Serial.printf("Sequence error: %d != %d+1\n", buffer_frame_packet_number, frame_packet_number);
      }
    }
    frame_packet_number = buffer_frame_packet_number;
    // first pass, we will take the data sent to us and convert the packet number into starting
    // X, Y positions.
    uint32_t starting_pixel_number = buffer_frame_packet_number * 30;
    uint16_t y = starting_pixel_number / tft.width();
    uint16_t x = starting_pixel_number % tft.width();

    // Now see if we can draw fits on the same row
    if ((x + 30) <= tft.width()) {
      tft.writeRect(x, y, 30, 1, pixel_data);
    } else {
      uint16_t w = tft.width() - x;
      tft.writeRect(x, y, w, 1, pixel_data);          // draw part on the calculated line
      tft.writeRect(0, y + 1, 30 - w, 1, &pixel_data[w]); // draw rest on next line
    }
    // See if that was the last expected packet
    if (frame_packet_number == (packets_per_frame - 1)) {
      uint32_t frame_time = msFrame;
      Serial.printf("Frame Completed in %u ms\n", frame_time);
      frame_packet_number = 0xffff;
    }
    msFrameTimeout = 0;
    digitalWrite(0, LOW);
  } else if ((frame_packet_number != 0xffff) && (msFrameTimeout > FRAME_TIMEOUT)) {
    Serial.printf("Frame Timeout: %d %d\n", frame_number, frame_packet_number);
    frame_packet_number = 0xffff; // only error once.
  }
}

I then created a USB host program using the RAW hid support, that when it receives Serial data, it draws a new page. Currently just cycles through whole color displays...
Code:
// Simple test of USB Host Mouse/Keyboard
//
// This example is in the public domain

#include "USBHost_t36.h"

USBHost myusb;
USBHub hub1(myusb);
USBHIDParser hid1(myusb);
USBHIDParser hid2(myusb);
USBHIDParser hid3(myusb);
RawHIDController rawhid1(myusb);
RawHIDController rawhid2(myusb, 0xffc90004);

USBDriver *drivers[] = {&hub1, &hid1, &hid2, &hid3,};
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))
const char * driver_names[CNT_DEVICES] = {"Hub1", "HID1", "HID2",};
bool driver_active[CNT_DEVICES] = {false, false};

// Lets also look at HID Input devices
USBHIDInput *hiddrivers[] = { &rawhid1, &rawhid2};
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))
const char * hid_driver_names[CNT_DEVICES] = {"RawHid1", "RawHid2"};
bool hid_driver_active[CNT_DEVICES] = {false, false};


void setup()
{
  pinMode(0, OUTPUT);
  digitalWrite(0, LOW);
  while (!Serial) ; // wait for Arduino Serial Monitor
  Serial.println("\n\nUSB Host Testing");
  Serial.println(sizeof(USBHub), DEC);
  myusb.begin();
  rawhid1.attachReceive(OnReceiveHidData);
  rawhid2.attachReceive(OnReceiveHidData);
}


void loop()
{
  myusb.Task();
  CheckUSBDeviceStatus();

  // If we get input from user, then lets try updating remote display
  if (Serial.available()) {
    while (Serial.read() != -1) ; // get rid of the input data.
    UpdateRAWHidDisplay();
  
  }

}

// Color definitions
#define ILI9341_BLACK       0x0000      /*   0,   0,   0 */
#define ILI9341_WHITE       0xFFFF      /* 255, 255, 255 */
#define ILI9341_RED         0xF800      /* 255,   0,   0 */
#define ILI9341_GREEN       0x07E0      /*   0, 255,   0 */
#define ILI9341_BLUE        0x001F      /*   0,   0, 255 */
#define ILI9341_YELLOW      0xFFE0      /* 255, 255,   0 */
#define ILI9341_ORANGE      0xFD20      /* 255, 165,   0 */
#define ILI9341_LIGHTGREY   0xC618      /* 192, 192, 192 */

uint16_t frame_number = 0; 
uint16_t colors[] = {ILI9341_RED, ILI9341_GREEN, ILI9341_BLUE, ILI9341_BLACK, ILI9341_WHITE, ILI9341_BLUE, ILI9341_YELLOW, ILI9341_ORANGE};

// first pass simple just full colors. 
uint16_t buffer[32];  // 64 byte buffer. 
#define PACKET_TIMEOUT 10
void UpdateRAWHidDisplay() {
  frame_number++;
  elapsedMillis msPacket;
  uint16_t color = colors[frame_number & 0x7];  // choose a color
  buffer[0] = frame_number;
  for (uint8_t i = 2; i < (sizeof(buffer)/sizeof(buffer[0])); i++) {
    buffer[i] = color;
  }
  bool packet_sent;
  for (buffer[1] = 0; buffer[1] < (320*240/30); buffer[1]++) {
    // Let try to output this buffer to the Raw Hid person...
    msPacket = 0;
    digitalWrite(0, HIGH);
    while (!(packet_sent = rawhid1.sendPacket((uint8_t*)buffer)) && msPacket < (PACKET_TIMEOUT)) ;
    digitalWrite(0, LOW);

  } 
  if (!packet_sent) {
    Serial.printf("Packet Timeout: %d %d\n", buffer[0], buffer[1]);
  }
}


bool OnReceiveHidData(uint32_t usage, const uint8_t *data, uint32_t len) {
  // Called for maybe both HIDS for rawhid basic test.  One is for the Teensy
  // to output to Serial. while still having Raw Hid...
  if (usage == 0xffc90004) {
    // Lets trim off trailing null characters.
    while ((len > 0) && (data[len - 1] == 0)) {
      len--;
    }
    if (len) {
      Serial.print("RawHid Serial: ");
      Serial.write(data, len);
    }
  } else {
    Serial.print("RawHID data: ");
    Serial.println(usage, HEX);
    while (len) {
      uint8_t cb = (len > 16) ? 16 : len;
      const uint8_t *p = data;
      uint8_t i;
      for (i = 0; i < cb; i++) {
        Serial.printf("%02x ", *p++);
      }
      Serial.print(": ");
      for (i = 0; i < cb; i++) {
        Serial.write(((*data >= ' ') && (*data <= '~')) ? *data : '.');
        data++;
      }
      len -= cb;
      Serial.println();
    }
  }

  return true;
}

void CheckUSBDeviceStatus() {
  for (uint8_t i = 0; i < CNT_DEVICES; i++) {
    if (*drivers[i] != driver_active[i]) {
      if (driver_active[i]) {
        Serial.printf("*** Device %s - disconnected ***\n", driver_names[i]);
        driver_active[i] = false;
      } else {
        Serial.printf("*** Device %s %x:%x - connected ***\n", driver_names[i], drivers[i]->idVendor(), drivers[i]->idProduct());
        driver_active[i] = true;

        const uint8_t *psz = drivers[i]->manufacturer();
        if (psz && *psz) Serial.printf("  manufacturer: %s\n", psz);
        psz = drivers[i]->product();
        if (psz && *psz) Serial.printf("  product: %s\n", psz);
        psz = drivers[i]->serialNumber();
        if (psz && *psz) Serial.printf("  Serial: %s\n", psz);
      }
    }
  }

  for (uint8_t i = 0; i < CNT_HIDDEVICES; i++) {
    if (*hiddrivers[i] != hid_driver_active[i]) {
      if (hid_driver_active[i]) {
        Serial.printf("*** HID Device %s - disconnected ***\n", hid_driver_names[i]);
        hid_driver_active[i] = false;
      } else {
        Serial.printf("*** HID Device %s %x:%x - connected ***\n", hid_driver_names[i], hiddrivers[i]->idVendor(), hiddrivers[i]->idProduct());
        hid_driver_active[i] = true;

        const uint8_t *psz = hiddrivers[i]->manufacturer();
        if (psz && *psz) Serial.printf("  manufacturer: %s\n", psz);
        psz = hiddrivers[i]->product();
        if (psz && *psz) Serial.printf("  product: %s\n", psz);
        psz = hiddrivers[i]->serialNumber();
        if (psz && *psz) Serial.printf("  Serial: %s\n", psz);
      }
    }
  }

}

The programs Run! and the display does update. Not as fast as I would like: Note: my test apps, also actually setup second Raw Hid device that the Serial.print outputs use. My host app receives those and outputs the messages to it's Serial object...

Some output:
Code:
*** HID Device RawHid2 16c0:486 - connected ***
  manufacturer: Teensyduino
  product: Teensyduino RawHID
  Serial: 2416110
RawHid Serial: New frame: 1 0
RawHid Serial: Frame Completed in 536810320 ms
New frame: 2 0
RawHid Serial: Frame Completed in 536810320 ms
New frame: 3 0
RawHid Serial: Frame Completed in 536810320 ms
RawHid Serial: New frame: 4 0
RawHid Serial: Frame Completed in 536810320 ms

The frame completion time is not correct... Need to check the output, but from Logic analyzer I believe it is taking about 2.5 seconds.
To send a full frame we send: 2560 packets

screenshot.jpg
From Logic Analyzer, you see that the first two raw packets queue more or less instantaneously, then the remaining ones have to wait until one completes before it can actually queue the data. Next up see if I can somehow speed it up...

Also then see if my implementation makes sense and if so to issue Pull Request.

But if anyone wants to play along...

Edit: Updated the first program to output the correct delta time
 
Last edited:
Update: Probably done with this for now.

As mentioned on the RawHid page: https://www.pjrc.com/teensy/rawhid.html
If you want to create a custom application, Raw HID is simple way to send 64 byte packets between your code on the Teensy and your application on the PC or Mac.
...
You can send up to 1000 packets per second in each direction.
So Makes sense that sending 2560 packets takes 2.56 seconds.
Code:
RawHid Serial: New frame: 11 0
RawHid Serial: Frame Completed in 2559 ms

I updated the program running RAWHID above to fix the output of how long it took...
 
SORRY I JUST REALIZE THIS IS GENERAL DISCUSSION
Tell me if there is a similar thread in Technical Questions, otherwise I will create one, thanks.

"Hi Kurt and everyone else !

I and two other guys are working on a project of modular controller including a Teensy 4.1 as a master unit and some Teensy 4.0 as slave units.
You can read this thread if you're interested in details :
https://forum.pjrc.com/threads/66362-Using-usbMIDI-with-I2C-without-saturating-I2C-bus?highlight=i2c+saturate
We had some trouble using usbMIDI on the master<>computer side together with i2C on the master<>slave side, because I2C is much slower than usbMIDI.
So Paul advised us to consider using USB instead.

After thinking a bit about the question, I felt the easier should be to make slaves RawHID devices in order to connect them to master USB Host.
But apart from Teensyduino examples, I found very few documentation on Teensy usbHost and RawHID, so my first step in this area are difficult.
As first test, I'm just trying to send "Hello!" every 2 seconds from my Teensy 4.0 RawHID slave to my Teensy 4.1 thru USB Host and print it in the Teensy 4.1 serial monitor. I used some examples codes on Teensyduino and parts of your codes but it doesn't work for the moment so any help would be appreciated.
My programmation skills remain weak so I apologize in advance if something obvious is going wrong.

Here is the Teensy 4.0 RawHID code :
Code:
void setup() {
  Serial.begin(9600);
  Serial.println(F("RawHID Example"));
  for (int i=0; i<7; i++) {
    pinMode(i, OUTPUT);
  }
}

// RawHID packets are always 64 bytes
byte buffer[64];
elapsedMillis msUntilNextSend;
unsigned int packetCount = 0;

void loop() {
  int n;
  // every 2 seconds, send a packet
  if (msUntilNextSend > 2000) {
    msUntilNextSend = msUntilNextSend - 2000;
    // first 2 bytes are a signature
    buffer[0] = 'H';
    buffer[1] = 'e';
    buffer[2] = 'l';
    buffer[3] = 'l';    
    buffer[4] = 'o';    
    buffer[5] = '!';
    for (uint8_t i=6; i<64; i++){
      buffer[i]=0;  
    }
    // actually send the packet
    n = RawHID.send(buffer, 0);
    if (n > 0) {
      Serial.print(F("Transmit packet "));
      Serial.println(packetCount);
      packetCount = packetCount + 1;
    } else {
      Serial.println(F("Unable to transmit packet"));
    }
  }
}

And here is the Teensy 4.1 USB Host code :

Code:
#include "USBHost_t36.h"
USBHost myusb;
USBHub hub1(myusb);
USBHIDParser hid1(myusb);
USBHIDParser hid2(myusb);
RawHIDController rawhid1(myusb);
RawHIDController rawhid2(myusb);

USBDriver *drivers[] = {&hub1, &hid1, &hid2};
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))
const char * driver_names[CNT_DEVICES] = {"Hub1", "HID1", "HID2",};
bool driver_active[CNT_DEVICES] = {false, false};

// Lets also look at HID Input devices
USBHIDInput *hiddrivers[] = { &rawhid1, &rawhid2};
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))
const char * hid_driver_names[CNT_HIDDEVICES] = {"RawHid1", "RawHid2"};
bool hid_driver_active[CNT_HIDDEVICES] = {false, false};

byte buffer[64];

void setup()
{
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
  while (!Serial) ; // wait for Arduino Serial Monitor
  Serial.println("MASTER");
  myusb.begin();
  rawhid1.attachReceive(OnReceiveHidData);
  rawhid2.attachReceive(OnReceiveHidData);
}

void loop()  
{
  myusb.Task();
  CheckUSBDeviceStatus();
}

void CheckUSBDeviceStatus() {   // Print out information about different devices.
  for (uint8_t i = 0; i < CNT_DEVICES; i++) {
    if (*drivers[i] != driver_active[i]) {
      if (driver_active[i]) {
        Serial.printf("*** Device %s - disconnected ***\n", driver_names[i]);
        driver_active[i] = false;
      } else {
        Serial.printf("*** Device %s %x:%x - connected ***\n", driver_names[i], drivers[i]->idVendor(), drivers[i]->idProduct());
        driver_active[i] = true;

        const uint8_t *psz = drivers[i]->manufacturer();
        if (psz && *psz) Serial.printf("  manufacturer: %s\n", psz);
        psz = drivers[i]->product();
        if (psz && *psz) Serial.printf("  product: %s\n", psz);
        psz = drivers[i]->serialNumber();
        if (psz && *psz) Serial.printf("  Serial: %s\n", psz);
      }
    }
  }

  for (uint8_t i = 0; i < CNT_HIDDEVICES; i++) {
    if (*hiddrivers[i] != hid_driver_active[i]) {
      if (hid_driver_active[i]) {
        Serial.printf("*** HID Device %s - disconnected ***\n", hid_driver_names[i]);
        hid_driver_active[i] = false;
      } else {
        Serial.printf("*** HID Device %s %x:%x - connected ***\n", hid_driver_names[i], hiddrivers[i]->idVendor(), hiddrivers[i]->idProduct());
        hid_driver_active[i] = true;

        const uint8_t *psz = hiddrivers[i]->manufacturer();
        if (psz && *psz) Serial.printf("  manufacturer: %s\n", psz);
        psz = hiddrivers[i]->product();
        if (psz && *psz) Serial.printf("  product: %s\n", psz);
        psz = hiddrivers[i]->serialNumber();
        if (psz && *psz) Serial.printf("  Serial: %s\n", psz);
      }
    }
  }
}

bool OnReceiveHidData(uint32_t usage, const uint8_t *data, uint32_t len) {
  // Called for maybe both HIDS for rawhid basic test.  One is for the Teensy
  // to output to Serial. while still having Raw Hid...
  Serial.print("HID data : ");
  Serial.write(data, len);
  return true;
}

I noticed that when I connect the Teensy 4.0 to the Teensy 4.1, I get "Devide HID" and not "HID Device RawHid" as you did in your example, maybe it's part of the problem :
Code:
*** Device HID1 160:486 - connected ***
  manufacturer: Teensyduino
  product: Teensyduino RawHID
  Serial: 8950760
*** Device HID2 160:486 - connected ***
  manufacturer: Teensyduino
  product: Teensyduino RawHID
  Serial: 8950760

Thank you in advance.
Best regards,"
 
Last edited:
SOLVED : I found out that messing with VENDOR_ID and PRODUCT_ID in usb_desc.h in /teensy/avr/cores/teensy4 was probably the issue.
I re-installed Teensyduino and now the Teensy 4 is well recognized as a RawHID device by the Teensy 4.1.
 
But the reason I made some changes in this header file is that I am looking for the smartest way to display in the serial monitor of the Teensy 4.1 :
- a "connect" command when each RawHID device is connected including a custom address (byte user variable between 1 and 16) and a custom type (const char between 'A' and 'E').
- a "disconnect" command when each RawHID device is disconnected including the same custom address and type
Like I did with I2C, I could ask Master to "poll" all possible addresses and consider device gone when timeout, but I feel like USB certainly offers a better way, using driver of something like that.
Do you have any idea ?
Thanks.
 
How do I send Raw data to connected HID device. I am playing now with HIDDeviceInfo example, the device connects and I get data from it but how to send data to it?

I see there is the

Code:
bool HIDrawController::hid_process_out_data(const Transfer_t *transfer)
{
  Serial.printf("HIDrawController::hid_process_out_data: %x\n", usage_);
  return true;
}

implemented, but what should be added to send Raw data out to the connected device?

This is the current complete code, a bit modified from the example, works just the same still. On around line 300 when button 1 press is detected I would like to send the raw data out (just to test)

Code:
// HIDDeviceInfo - Simple HID device example
// 
// This Simple test sketch is setup to print out HID information about a device
// The other two tabs are a simple C++ subclass of the USBHIDInput class that is part 
// of the USBHost_t36 library.  
//
// This subclass simply tries to connect to each different HID object and
// the only thing it does is to try to print out all of the data it receives
// in a reasonable way. 
//
// The idea is that with the output from this sketch we can hopefully add support 
// for some additional devices that are not currently supported or allows you to 
// develop your own. 
// 
// You can use Serial Input to control how much data is displayed per each HID packet
// received by the sketch.
//
// By Default it displays both the RAW (Hex dump) of the data received, as well
// as the data as the HID interpreter walks through the data into the individual
// fields, which we then print out.  
//
// There are options to turn off some of this output, also an option that you can
// toggle on or off (C) to only try to show the changed fields.   
//
// This example is in the public domain

#include <USBHost_t36.h>





class HIDrawController : public USBHIDInput {
public:
  HIDrawController(USBHost &host, uint32_t usage = 0) : fixed_usage_(usage) { init(); }
  uint32_t usage(void) {return usage_;}
  static bool show_raw_data;
  static bool show_formated_data;
  static bool changed_data_only;
  
protected:
  virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage);
  virtual bool hid_process_in_data(const Transfer_t *transfer);
  virtual bool hid_process_out_data(const Transfer_t *transfer);
  virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax);
  virtual void hid_input_data(uint32_t usage, int32_t value);
  virtual void hid_input_end();
  virtual void disconnect_collection(Device_t *dev);
private:
  void init();
  USBHIDParser *driver_;
  uint8_t collections_claimed = 0;
  volatile int hid_input_begin_level_ = 0;
  uint32_t fixed_usage_;
  uint32_t usage_ = 0;
  // Track changing fields. 
  const static int MAX_CHANGE_TRACKED = 512;
  uint32_t usages_[MAX_CHANGE_TRACKED];
  int32_t values_[MAX_CHANGE_TRACKED];
  int count_usages_ = 0;
  int index_usages_ = 0;
  
  // See if we can contribute transfers
  Transfer_t mytransfers[2] __attribute__ ((aligned(32)));
};


class USBDeviceInfo : public USBDriver {
public:
  USBDeviceInfo(USBHost &host) { init();}

protected:
  virtual bool claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len);
  virtual void disconnect() {};
  void init();
};

USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBDeviceInfo dinfo(myusb); // will never claim anything... 
USBHIDParser hid1(myusb);
USBHIDParser hid2(myusb);
USBHIDParser hid3(myusb);
USBHIDParser hid4(myusb);
USBHIDParser hid5(myusb);

HIDrawController hdc1(myusb);
HIDrawController hdc2(myusb);
HIDrawController hdc3(myusb);
HIDrawController hdc4(myusb);
HIDrawController hdc5(myusb);

USBDriver *drivers[] = {&hub1, &hub2, &hid1, &hid2, &hid3, &hid4, &hid5};
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))
const char * driver_names[CNT_DEVICES] = {"Hub1", "Hub2", "HID1" , "HID2", "HID3", "HID4", "HID5"};
bool driver_active[CNT_DEVICES] = {false, false, false, false};

// Lets also look at HID Input devices
USBHIDInput *hiddrivers[] = {&hdc1, &hdc2,  &hdc3, &hdc4, &hdc5};
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))
const char * hid_driver_names[CNT_DEVICES] = {"hdc1", "hdc2", "hdc3", "hdc4", "hdc5"};
bool hid_driver_active[CNT_DEVICES] = {false, false};
bool show_changed_only = false;
void setup()
{
  Serial1.begin(2000000);
  while (!Serial) ; // wait for Arduino Serial Monitor
  Serial.println("\n\nUSB HID Device Info Program");
  Serial.println("\nThis Sketch shows information about plugged in HID devices");
  Serial.println("\n*** You can control the output by simple character input to Serial ***");
  Serial.println("R - Turns on or off showing the raw data");
  Serial.println("C - Toggles showing changed data only on or off");
  Serial.println("<anything else> - toggles showing the Hid formatted breakdown of the data\n");

  myusb.begin();
}


void loop()
{
  myusb.Task();

  if (Serial.available()) {
    int ch = Serial.read(); // get the first char.
    while (Serial.read() != -1) ;
    if (ch == 'r' || (ch == 'R')) {
      if (HIDrawController::show_raw_data) {
        HIDrawController::show_raw_data = false;
        HIDrawController::show_formated_data = true;
        Serial.println("\n*** Turn off RAW output formatted data is on ***\n");
      } else {
        HIDrawController::show_raw_data = true;
        Serial.println("\n*** Turn on RAW output ***\n");
      }
    } else if (ch == 'C' || (ch == 'c')) {
      if (HIDrawController::changed_data_only) {
        HIDrawController::changed_data_only = false;
        Serial.println("***\n Now Showing all data ***\n");
      } else {
        HIDrawController::changed_data_only = true;
        Serial.println("***\n Now Showing changed data only ***\n");

      }
    } else {
      if (HIDrawController::show_formated_data) {
        HIDrawController::show_formated_data = false;
        HIDrawController::show_raw_data = true;  // At least make sure raw raw data is output
        Serial.println("\n*** Turn off formatted output formated HID data is on ***\n");
      } else {
        HIDrawController::show_formated_data = true;
        Serial.println("\n*** Turn on formated output ***\n");
      }
    }
  }

  for (uint8_t i = 0; i < CNT_DEVICES; i++) {
    if (*drivers[i] != driver_active[i]) {
      if (driver_active[i]) {
        Serial.printf("*** Device % s - disconnected ***\n", driver_names[i]);
        driver_active[i] = false;
      } else {
        Serial.printf("*** Device % s % x: % x - connected ***\n", driver_names[i], drivers[i]->idVendor(), drivers[i]->idProduct());
        driver_active[i] = true;

        const uint8_t *psz = drivers[i]->manufacturer();
        if (psz && *psz) Serial.printf("  manufacturer: % s\n", psz);
        psz = drivers[i]->product();
        if (psz && *psz) Serial.printf("  product: % s\n", psz);
        psz = drivers[i]->serialNumber();
        if (psz && *psz) Serial.printf("  Serial: % s\n", psz);
      }
    }
  }

  for (uint8_t i = 0; i < CNT_HIDDEVICES; i++) {
    if (*hiddrivers[i] != hid_driver_active[i]) {
      if (hid_driver_active[i]) {
        Serial.printf("*** HID Device % s - disconnected ***\n", hid_driver_names[i]);
        hid_driver_active[i] = false;
      } else {
        Serial.printf("*** HID Device % s % x: % x - connected ***\n", hid_driver_names[i], hiddrivers[i]->idVendor(), hiddrivers[i]->idProduct());
        hid_driver_active[i] = true;

        const uint8_t *psz = hiddrivers[i]->manufacturer();
        if (psz && *psz) Serial.printf("  manufacturer: % s\n", psz);
        psz = hiddrivers[i]->product();
        if (psz && *psz) Serial.printf("  product: % s\n", psz);
        psz = hiddrivers[i]->serialNumber();
        if (psz && *psz) Serial.printf("  Serial: % s\n", psz);
      }
    }
  }
}



bool HIDrawController::show_raw_data = true;
bool HIDrawController::show_formated_data = true;
bool HIDrawController::changed_data_only = false;

void HIDrawController::init()
{
  USBHost::contribute_Transfers(mytransfers, sizeof(mytransfers) / sizeof(Transfer_t));
  USBHIDParser::driver_ready_for_hid_collection(this);
}

hidclaim_t HIDrawController::claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage)
{
  // only claim RAWHID devices currently: 16c0:0486
  Serial.printf("HIDrawController Claim: %x:%x usage: %x", dev->idVendor, dev->idProduct, topusage);
  if (mydevice != NULL && dev != mydevice) {
    Serial.println("- NO (Device)");
    return CLAIM_NO;
  }
  if (usage_ && (usage_ != topusage)) {
    Serial.printf(" - NO (Usage: %x)\n");
    return CLAIM_NO;      // Only claim one
  }
  mydevice = dev;
  collections_claimed++;
  usage_ = topusage;
  driver_ = driver; // remember the driver.
  Serial.println(" - Yes");
  return CLAIM_INTERFACE;  // We want
}

void HIDrawController::disconnect_collection(Device_t *dev)
{
  if (--collections_claimed == 0) {
    mydevice = NULL;
    usage_ = 0;
  }
}

void dump_hexbytes(const void *ptr, uint32_t len)
{
  if (ptr == NULL || len == 0) return;
  uint32_t count = 0;
//  if (len > 64) len = 64; // don't go off deep end...
  const uint8_t *p = (const uint8_t *)ptr;
  while (len--) {
    if (*p < 16) Serial.print('0');
    Serial.print(*p++, HEX);
    count++;
    if (((count & 0x1f) == 0) && len) Serial.print("\n");
    else Serial.print(' ');
  } 
  Serial.println();
}

bool HIDrawController::hid_process_in_data(const Transfer_t *transfer)
{
  // return true if we are not showing formated data...
  hid_input_begin_level_ = 0; // always make sure we reset to 0
  count_usages_ = index_usages_;  // remember how many we output for this one
  index_usages_ = 0;        // reset the index back to zero

  Serial.printf("HID(%x)", usage_);
  if (show_raw_data) {
    Serial.print(": ");
    dump_hexbytes(transfer->buffer, transfer->length);
  }
  else Serial.println();

  return !show_formated_data;
}

bool HIDrawController::hid_process_out_data(const Transfer_t *transfer)
{
  Serial.printf("HIDrawController::hid_process_out_data: %x\n", usage_);
  return true;
}

void indent_level(int level) {
  if ((level > 5) || (level < 0)) return; // bail if something is off...
  while (level--) Serial.print("  ");
}

void HIDrawController::hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax)
{
  // Lets do simplified data for changed only
  if (changed_data_only) return;

  indent_level(hid_input_begin_level_);
  Serial.printf("Begin topusage:%x type:%x min:%d max:%d\n", topusage, type, lgmin, lgmax);
  if (hid_input_begin_level_ < 2)
    hid_input_begin_level_++;
}

void HIDrawController::hid_input_data(uint32_t usage, int32_t value)
{

  bool output_data = !changed_data_only;

  // See if something changed.
  if (index_usages_ < count_usages_) {
    if ((usage != usages_[index_usages_]) || (value != values_[index_usages_])) {
      output_data = true;
    }
  } else {
    output_data = true;
  }
  if (index_usages_ < MAX_CHANGE_TRACKED) {
    usages_[index_usages_] = usage;
    values_[index_usages_] = value;
    index_usages_++;
  }

  if (output_data) {
    indent_level(hid_input_begin_level_);
    Serial.printf("usage=%X, value=%d", usage, value);
    if ((value >= ' ') && (value <= '~')) Serial.printf(":%c", value);

    // maybe print out some information about some of the Usage numbers that we know about
    // The information comes from the USB document, HID Usage Tables 
    // https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf
    
    uint16_t usage_page = usage >> 16;
    usage = usage & 0xffff; // keep the lower part
    switch (usage_page) {

      case 9: // Button
        Serial.printf(" (BUTTON %d)", usage);
        break;

//Stream deck dim display comand
 uint8_t D1_out[] ={
    0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
   //     if(usage == 1) sendPacket(D1_out);




              default: 
        Serial.println(" UNKNOWN");
        break;
     
    }
    Serial.println();
  }
}

void HIDrawController::hid_input_end()
{
  // Lets do simplified data for changed only
  if (changed_data_only) return;
  hid_input_begin_level_--;
  indent_level(hid_input_begin_level_);
  Serial.println("END:");
}



extern void dump_hexbytes(const void *ptr, uint32_t len);

static void println(const char *title, uint32_t val, uint8_t b = DEC) {
  Serial.print(title);
  Serial.println(val, b); 
}

static void print(const char *title, uint32_t val, uint8_t b = DEC) {
  Serial.print(title);
  Serial.print(val, b); 
}

void USBDeviceInfo::init()
{
  driver_ready_for_device(this);
}

// Again this class is solely to display as much information about a device as we can...
// This all comes from the information passed to it through the claim method.
bool USBDeviceInfo::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) {
  println("\nUSBDeviceInfo claim this=", (uint32_t)this, HEX);


  if (type == 0) {
    // At device level
    Serial.println("\n****************************************");
    Serial.println("** Device Level **");
    println("  vid=", dev->idVendor, HEX);
    println("  pid=", dev->idProduct, HEX);
    println("  bDeviceClass = ", dev->bDeviceClass);
      println("  bDeviceSubClass = ", dev->bDeviceSubClass);
      println("  bDeviceProtocol = ", dev->bDeviceProtocol);
    dump_hexbytes(descriptors, len);
    return false;
  }
  // only claim at interface level
  Serial.println("\n****************************************");
  Serial.println("** Interface Level **");
  dump_hexbytes(descriptors, len);
  if (len < 9+9+7) return false;

  // interface descriptor
  uint32_t numendpoint = descriptors[4];
  println(" bInterfaceNumber = ",  descriptors[2]);
  println(" number end points = ",  numendpoint);
  println(" bInterfaceClass =    ", descriptors[5]);
  println(" bInterfaceSubClass = ", descriptors[6]);
  switch (descriptors[5]) {
    case 2: Serial.println("    Communicatons and CDC"); break;
    case 3:
      if (descriptors[6] == 1) Serial.println("    HID (BOOT)");
      else Serial.println("    HID"); 
      break;
    case 0xa: Serial.println("    CDC-Data"); break;
  }

  println(" bInterfaceProtocol = ", descriptors[7]);

  //if (numendpoint < 1 || numendpoint > 2) return false;

  // hid interface descriptor
  uint32_t offset = 9;
  uint32_t hidlen = descriptors[offset];
  uint16_t descsize = 0;

  if (hidlen < 9) return false;

  if (descriptors[10] == 33)  { // This is a HID Descriptor type. return false; // descriptor type, 33=HID
    if (descriptors[14] < 1) return false;  // must be at least 1 extra descriptor
    if (hidlen != (uint32_t)(6 + descriptors[14] * 3)) return false; // must be correct size
    if (9 + hidlen > len) return false;
    uint32_t i=0;
    while (1) {
      if (descriptors[15 + i * 3] == 34) { // found HID report descriptor
        descsize = descriptors[16 + i * 3] | (descriptors[17 + i * 3] << 8);
        println("report descriptor size = ", descsize);
        break;
      }
      i++;
      if (i >= descriptors[14]) break;;
    }
    offset += hidlen;
  }
  // endpoint descriptor(s)
  while (numendpoint && (offset < len)) {
    if ((descriptors[offset] == 7) && (descriptors[offset+1] == 5)) {
      // we have an end point:
      println("  endpoint = ", descriptors[offset+2], HEX);
      print(  "    attributes = ", descriptors[offset+3], HEX);
      switch (descriptors[offset+3] & 0x3) {
        case 0: Serial.println(" Control"); break;
        case 1: Serial.println(" Isochronous"); break;
        case 2: Serial.println(" Bulk"); break;
        case 3: Serial.println(" Interrupt"); break;
      }
      uint32_t size = descriptors[offset+4] | (descriptors[offset+5] << 8);
      println("    size = ", size);
      println("    interval = ", descriptors[offset+6]);
      numendpoint--;
    }
    offset += descriptors[offset];
  }
  return false;
}
 
Sorry,

The examples are not really exercising rawhid that much right now.
Many of them have stuff in them, that did: for example the Mouse.ino example
has code that will echo stuff that it receives by Serial to the rawhid.

But later code was added to have the Serial stuff be used as a quick and dirty command setup, and all Serial stuff is processed before the rawhid has a chance to work:

Code:
  // See if we have some RAW data
  if (rawhid1) {
    int ch;
    uint8_t buffer[64];
    uint8_t count_chars = 0;
    memset(buffer, 0, sizeof(buffer));
    if (Serial.available()) {
      while (((ch = Serial.read()) != -1) && (count_chars < sizeof(buffer))) {
        buffer[count_chars++] = ch;
      }
      rawhid1.sendPacket(buffer);
    }
  }
That is sendPacket will send a 64 byte packet.

Side Note: At one point (maybe there still is). I had a Pull Request to add the ability for RawHid on T4.x to be able to send 512 byte records.
Don't see it now. Also, unclear if it will be incorporated as then all of the hosts code needs to be updated as well. I did have versions that ran on Windows and Linux, but...
 
I tried now this

Code:
 case 9: // Button
        Serial.printf(" (BUTTON %d)", usage);
       
        if(usage == 1 && value == 1){
          
        //Stream deck dim display comand
         uint8_t D1_out[] ={  0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,};
    
          hid1.sendPacket(D1_out);

        }
        break;

But instead dimming the display, the devise resets. The device information is

Code:
USBDeviceInfo claim this=2000AAC8

****************************************
** Device Level **
  vid=FD9
  pid=60
  bDeviceClass = 0
  bDeviceSubClass = 0
  bDeviceProtocol = 0
09 04 00 00 02 03 00 00 00 09 21 11 01 00 01 22 F8 00 07 05 81 03 00 02 01 07 05 02 03 00 02 01 

USBDeviceInfo claim this=2000AAC8

****************************************
** Interface Level **
09 04 00 00 02 03 00 00 00 09 21 11 01 00 01 22 F8 00 07 05 81 03 00 02 01 07 05 02 03 00 02 01 
 bInterfaceNumber = 0
 number end points = 2
 bInterfaceClass =    3
 bInterfaceSubClass = 0
    HID
 bInterfaceProtocol = 0
report descriptor size = 248
  endpoint = 81
    attributes = 3 Interrupt
    size = 512
    interval = 1
  endpoint = 2
    attributes = 3 Interrupt
    size = 512
    interval = 1
*** Device HID1 fd9: 60 - connected ***
  manufacturer: Elgato Systems
  product: Stream Deck
  Serial: AL01H1A08927
HIDrawController Claim: fd9:60 usage: c0001 - Yes
*** HID Device hdc1 fd9: 60 - connected ***
  manufacturer: Elgato Systems
  product: Stream Deck
  Serial: AL01H1A08927


What is the hdc1 should I use that instead to write, but currently with it does not compile. edit: HDC Hard Disk Controller? I did not know it has such.

Also the size is 512 so I guess the 512 buffer would be great, how to get that?
 
Note: The HID device info sketch, more or less bypasses all of the other classes, like rawhid, serial, mouse... And is mostly there to try to get things in a more generic way, with the idea of trying to gather additional information, like what is in the HID descriptors and the like, making it easier to add support into the other objects.

Note: what the rawhid object does with this:
Code:
bool RawHIDController::sendPacket(const uint8_t *buffer) 
{
	if (!driver_) return false;
	return driver_->sendPacket(buffer);
}

As for what the device might do with it. That I can not say. It may be expecting data in a certain format or specific size packet or...
 
It seems that the problem of the restart was actually teensy restarting and reason that the array was smaller than 64. 512 size array seems also to not to crash. But either is having the wanted effect on device, i.e device does nothing. so not sure what. The format I have verified from many places.

Maybe the problem is that Teensy anyway sends 64 size packet and device expects 512, how to solve this?

When the send is trickeries, teensy reports (i.e the modified as above described HIDDeviceInfo reports)

Code:
 usage=9000A, value=0 (BUTTON 10)
  usage=9000B, value=0 (BUTTON 11)
  usage=9000C, value=0 (BUTTON 12)
  usage=9000D, value=0 (BUTTON 13)
  usage=9000E, value=0 (BUTTON 14)
  usage=9000F, value=0 (BUTTON 15)
  usage=90010, value=0 (BUTTON 16)
END:
>>>USBHIDParser::out_data
HIDrawController::hid_process_out_data: c0001
HID(c0001): 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
F8 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 A0 9C 00 20 00 00 00 00 00 00 00 00 00 00 00 00
 
Should I somehow then include the rawhid to the HID device info sketch. That sketch provides easy way the needed information from the device (button presses) The challenge is now to send images to the buttons, but thought to try with simpler command (dim screen) first.

I am trying to do this with Teensy 4.1 https://github.com/abcminiuser/python-elgato-streamdeck

edit: looking the response

>>>USBHIDParser::eek:ut_data
HIDrawController::hid_process_out_data: c0001

what is the USBHIDParser::eek:ut_data doing for the data, so not raw data with this hid1.sendPacket(D1_out);

should I define the RAW HID like RawHIDController rawhid1(myusb);

and maybe rawhid1.attachReceive(OnReceiveHidData); to also receive with it?

This is all very confusing for me doing the HID part of USB for the first time and not much experienced on this kind of coding. Camera PTP I succeeded to do some time ago with the USBHost_t36 but that was also much error trial and guessing, and some help received.
 
Last edited:
I just can not figure out how to do the raw hid data sending on this. otherwise it works

Code:
#include <USBHost_t36.h>

class HID_Device : public USBHIDInput {
public:
  HID_Device(USBHost &host, uint32_t usage = 0) : fixed_usage_(usage) { init(); }
  uint32_t usage(void) {return usage_;}
  static bool show_raw_data;
  static bool show_formated_data;
  static bool changed_data_only;
  //static void hid_send_raw_data(uint8_t D1_out_[]){RawHIDController::sendPacket(D1_out_);};
  
//protected:
  virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage);
  virtual bool hid_process_in_data(const Transfer_t *transfer);
  virtual bool hid_process_out_data(const Transfer_t *transfer);
  virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax);
  virtual void hid_input_data(uint32_t usage, int32_t value);
  virtual void hid_input_end();
  virtual void disconnect_collection(Device_t *dev);
  
//private:
  void init();
  USBHIDParser *driver_;
  uint8_t collections_claimed = 0;
  volatile int hid_input_begin_level_ = 0;
  uint32_t fixed_usage_;
  uint32_t usage_ = 0;
  // Track changing fields. 
  const static int MAX_CHANGE_TRACKED = 512;
  uint32_t usages_[MAX_CHANGE_TRACKED];
  int32_t values_[MAX_CHANGE_TRACKED];
  int count_usages_ = 0;
  int index_usages_ = 0;
  
  // See if we can contribute transfers
  Transfer_t mytransfers[2] __attribute__ ((aligned(32)));
};



USBHost USB_HID;
USBHub hub1(USB_HID);
USBHIDParser hid1(USB_HID);
RawHIDController rawhid1(USB_HID);

HID_Device STREAMDECK(USB_HID);


USBDriver *drivers[] = {&hub1, &hid1};
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))
const char * driver_names[CNT_DEVICES] = {"Hub1",  "HID1" };
bool driver_active[CNT_DEVICES] = {false, false};

// Lets also look at HID Input devices
USBHIDInput *hiddrivers[] = {&STREAMDECK,  &rawhid1};
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))
const char * hid_driver_names[CNT_DEVICES] = {"STREAMDECK", "rawhid1" };
bool hid_driver_active[CNT_DEVICES] = {false, false};
bool show_changed_only = false;


uint8_t D1_out[512]; 

 
void setup()
{
  
  while (!Serial) ; // wait for Arduino Serial Monitor


  USB_HID.begin();
 // rawhid1.attachReceive(OnReceiveHidData);

  
}


void loop()
{
  USB_HID.Task();



  for (uint8_t i = 0; i < CNT_DEVICES; i++) {
    if (*drivers[i] != driver_active[i]) {
      if (driver_active[i]) {
        Serial.printf("*** Device % s - disconnected ***\n", driver_names[i]);
        driver_active[i] = false;
      } else {
        Serial.printf("*** Device % s % x: % x - connected ***\n", driver_names[i], drivers[i]->idVendor(), drivers[i]->idProduct());
        driver_active[i] = true;

        const uint8_t *psz = drivers[i]->manufacturer();
        if (psz && *psz) Serial.printf("  manufacturer: % s\n", psz);
        psz = drivers[i]->product();
        if (psz && *psz) Serial.printf("  product: % s\n", psz);
        psz = drivers[i]->serialNumber();
        if (psz && *psz) Serial.printf("  Serial: % s\n", psz);
      }
    }
  }

  for (uint8_t i = 0; i < CNT_HIDDEVICES; i++) {
    if (*hiddrivers[i] != hid_driver_active[i]) {
      if (hid_driver_active[i]) {
        Serial.printf("*** HID Device % s - disconnected ***\n", hid_driver_names[i]);
        hid_driver_active[i] = false;
      } else {
        Serial.printf("*** HID Device % s % x: % x - connected ***\n", hid_driver_names[i], hiddrivers[i]->idVendor(), hiddrivers[i]->idProduct());
        hid_driver_active[i] = true;

        const uint8_t *psz = hiddrivers[i]->manufacturer();
        if (psz && *psz) Serial.printf("  manufacturer: % s\n", psz);
        psz = hiddrivers[i]->product();
        if (psz && *psz) Serial.printf("  product: % s\n", psz);
        psz = hiddrivers[i]->serialNumber();
        if (psz && *psz) Serial.printf("  Serial: % s\n", psz);
      }
    }
  }
}



bool HID_Device::show_raw_data = true;
bool HID_Device::show_formated_data = true;
bool HID_Device::changed_data_only = false;

void HID_Device::init()
{
  USBHost::contribute_Transfers(mytransfers, sizeof(mytransfers) / sizeof(Transfer_t));
  USBHIDParser::driver_ready_for_hid_collection(this);
}

hidclaim_t HID_Device::claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage)
{
  // only claim RAWHID devices currently: 16c0:0486
  Serial.printf("HID_Device Claim: %x:%x usage: %x", dev->idVendor, dev->idProduct, topusage);
  if (mydevice != NULL && dev != mydevice) {
    Serial.println("- NO (Device)");
    return CLAIM_NO;
  }
  if (usage_ && (usage_ != topusage)) {
    Serial.printf(" - NO (Usage: %x)\n");
    return CLAIM_NO;      // Only claim one
  }
  mydevice = dev;
  collections_claimed++;
  usage_ = topusage;
  driver_ = driver; // remember the driver.
  Serial.println(" - Yes");
  return CLAIM_INTERFACE;  // We want
}

void HID_Device::disconnect_collection(Device_t *dev)
{
  if (--collections_claimed == 0) {
    mydevice = NULL;
    usage_ = 0;
  }
}

void dump_hexbytes(const void *ptr, uint32_t len)
{
  if (ptr == NULL || len == 0) return;
  uint32_t count = 0;
//  if (len > 64) len = 64; // don't go off deep end...
  const uint8_t *p = (const uint8_t *)ptr;
  while (len--) {
    if (*p < 16) Serial.print('0');
    Serial.print(*p++, HEX);
    count++;
    if (((count & 0x1f) == 0) && len) Serial.print("\n");
    else Serial.print(' ');
  } 
  Serial.println();
}

bool HID_Device::hid_process_in_data(const Transfer_t *transfer)
{
  // return true if we are not showing formated data...
  hid_input_begin_level_ = 0; // always make sure we reset to 0
  count_usages_ = index_usages_;  // remember how many we output for this one
  index_usages_ = 0;        // reset the index back to zero

  Serial.printf("HID(%x)", usage_);
  if (show_raw_data) {
    Serial.print(": ");
    dump_hexbytes(transfer->buffer, transfer->length);
  }
  else Serial.println();

  return !show_formated_data;
}

bool HID_Device::hid_process_out_data(const Transfer_t *transfer)
{
  Serial.printf("HID_Device::hid_process_out_data: %x\n", usage_);
//sendPacket(Transfer_t);

  return true;
}

void indent_level(int level) {
  if ((level > 5) || (level < 0)) return; // bail if something is off...
  while (level--) Serial.print("  ");
}

void HID_Device::hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax)
{
  // Lets do simplified data for changed only
  if (changed_data_only) return;

  indent_level(hid_input_begin_level_);
  Serial.printf("Begin topusage:%x type:%x min:%d max:%d\n", topusage, type, lgmin, lgmax);
  if (hid_input_begin_level_ < 2)
    hid_input_begin_level_++;
}

void HID_Device::hid_input_data(uint32_t usage, int32_t value)
{

  bool output_data = !changed_data_only;

  // See if something changed.
  if (index_usages_ < count_usages_) {
    if ((usage != usages_[index_usages_]) || (value != values_[index_usages_])) {
      output_data = true;
    }
  } else {
    output_data = true;
  }
  if (index_usages_ < MAX_CHANGE_TRACKED) {
    usages_[index_usages_] = usage;
    values_[index_usages_] = value;
    index_usages_++;
  }

  if (output_data) {
    indent_level(hid_input_begin_level_);
    Serial.printf("usage=%X, value=%d", usage, value);
    if ((value >= ' ') && (value <= '~')) Serial.printf(":%c", value);

    // maybe print out some information about some of the Usage numbers that we know about
    // The information comes from the USB document, HID Usage Tables 
    // https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf
    
    uint16_t usage_page = usage >> 16;
    usage = usage & 0xffff; // keep the lower part
    switch (usage_page) {

      case 9: // Button
        Serial.printf(" (BUTTON %d)", usage);
       
        if(usage == 1 && value == 1){
          
        //Stream deck dim display comand
         //uint8_t D1_out[] ={  0x05, 0x55, 0xaa, 0xd1, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
       
        
           D1_out[0]=0x05;
           D1_out[1]=0x55;
           D1_out[2]=0xAA;
           D1_out[3]=0xD1;
           D1_out[4]=0x01;
           D1_out[5]=0x00;//brightness
           
            //STREAMDECK.hid_send_raw_data(D1_out);
              hid1.sendPacket(D1_out);
            //  rawhid1.sendPacket(D1_out);
            
           Serial.println(" SEND");
           delay(200);
           
        }
        break;

        default: 
        Serial.println(" UNKNOWN");
        break;
     
    }
    Serial.println();
  }
}

void HID_Device::hid_input_end()
{
  // Lets do simplified data for changed only
  if (changed_data_only) return;
  hid_input_begin_level_--;
  indent_level(hid_input_begin_level_);
  Serial.println("END:");
}



extern void dump_hexbytes(const void *ptr, uint32_t len);

static void println(const char *title, uint32_t val, uint8_t b = DEC) {
  Serial.print(title);
  Serial.println(val, b); 
}

static void print(const char *title, uint32_t val, uint8_t b = DEC) {
  Serial.print(title);
  Serial.print(val, b); 
}






bool OnReceiveHidData(uint32_t usage, const uint8_t *data, uint32_t len) {
  // Called for maybe both HIDS for rawhid basic test.  One is for the Teensy
  // to output to Serial. while still having Raw Hid...
  if (usage == 0xc0001) {
    // Lets trim off trailing null characters.
    while ((len > 0) && (data[len - 1] == 0)) {
      len--;
    }
    if (len) {
      Serial.print("RawHid Serial: ");
      Serial.write(data, len);
    }
  } else {
    Serial.print("RawHID data: ");
    Serial.println(usage, HEX);
    while (len) {
      uint8_t cb = (len > 16) ? 16 : len;
      const uint8_t *p = data;
      uint8_t i;
      for (i = 0; i < cb; i++) {
        Serial.printf("%02x ", *p++);
      }
      Serial.print(": ");
      for (i = 0; i < cb; i++) {
        Serial.write(((*data >= ' ') && (*data <= '~')) ? *data : '.');
        data++;
      }
      len -= cb;
      Serial.println();
    }
  }

  return true;
}

??
//STREAMDECK.hid_send_raw_data(D1_out);
hid1.sendPacket(D1_out);
// rawhid1.sendPacket(D1_out);

Also how are USBHIDParser hid1(USB_HID); and HID_Device STREAMDECK(USB_HID); supposed to work, why I need other definition than HID_Device STREAMDECK(USB_HID);
 
Some progress. it seems the length of the message can and must be defined.

hid1.sendPacket(D1_out,32);

However the control does not work as expected, but I can see it changes the response, and does not shutdown.

is the sendPacket function different if length is defined? But I still think it is not sending the raw data.

I added the printout dump_hexbytes(transfer->buffer, transfer->length); and it looks outgoing bytes are as meant to be , so maybe the problem os elsewhere, different commands on new FW etc.

Code:
bool HID_Device::hid_process_out_data(const Transfer_t *transfer)
{
  Serial.printf("HID_Device::hid_process_out_data: %x\n", usage_);
dump_hexbytes(transfer->buffer, transfer->length);

  return true;
}
 
Last edited:
This is how the image should be sent

Images are sent to the device in two packets, the first containing 2583 pixels (7749 bytes) and the second containing the remaining 2601 pixels (7803 bytes). Each packet is a total of 8191 bytes.

is it possible to do, what should be changed on the library?
 
Sorry hard to say:

First you say they should be sent in two packets, but each physical RAW-HID packet is 64 bytes. I do have a version that can send 512 bytes, the USB Max af USB 2 high speed.
RAWHID has some specific meaning, like defined in: https://www.pjrc.com/teensy/rawhid.html

The rawhid code does send packets using the sendPacket code, which can allow you to tell USB that the actual size of data to send in the packet up to the size of TX defined in the USB desriptors TX pipe definition, provided by the device.

So now you are instead talking about some logical packet. The question, is what is the actual logical format of these packets? Is it something as simple as, the first 32 byte packet contains a count of bytes and then it keeps decrementing by 32 and last packet maybe needs to be sized for exact remaining number of pixels.

Is there some specific place in the python code project that shows what they are doing?
 
If my quick glance through their code is correct.

Looks like probably they are sending a bitmap file (.BMP) data.

That is their blank one is:
Code:
    # 72 x 72 black BMP
    BLANK_KEY_IMAGE = [
        0x42, 0x4d, 0xf6, 0x3c, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00,
        0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x48, 0x00,
        0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00,
        0x00, 0x00, 0xc0, 0x3c, 0x00, 0x00, 0xc4, 0x0e,
        0x00, 0x00, 0xc4, 0x0e, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    ] + [0] * (KEY_PIXEL_WIDTH * KEY_PIXEL_HEIGHT * 3)
I did not double check that data but pretty sure the first two bytes are the BMP signature bytes.

Looks like the header is 54 bytes Plus 72*72*3 = 15606 bytes

Then it looks like it wants to divide this into two logical packets:
image_report_payload_length = len(image) // 2
or 7803 bytes per pay load.

It then wants to add on a header to each of these logical packets
Code:
0x02,
                0x01,
                page_number + 1,
                0,
                1 if this_length == bytes_remaining else 0,
                key + 1,
                0,
                0,
                0,
                0,
                0,
                0,
                0,
                0,
                0,
                0,
            ]
Which quick count is 16 bytes.
So where you have a key...

It then sets up a padding,
padding = bytearray(self.IMAGE_REPORT_LENGTH - len(payload))
IMAGE_REPORT_LENGTH = 8191
So it looks like every packet is 8191 bytes and in this case
8191 - (7803+16) = 372 bytes of padding:
As they write out: self.device.write(payload + padding)


You need to probably try to emulate this. packing of data
 
Yes I could, make them several 64 byte messages, but would that add anything else between the bytes than just a minor delay. if not, it should work.

But still having problems with the smaller packages also (17 bytes long) there must be something wrong on the data is sent or then the device FW does not work the same as can bee seen from the existing code.
 
BTW, any idea what this is "] + [0] * (KEY_PIXEL_WIDTH * KEY_PIXEL_HEIGHT * 3)" i.e how to write that array in C

Code:
BLANK_KEY_IMAGE = [
        0x42, 0x4d, 0xf6, 0x3c, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00,
        0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x48, 0x00,
        0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00,
        0x00, 0x00, 0xc0, 0x3c, 0x00, 0x00, 0xc4, 0x0e,
        0x00, 0x00, 0xc4, 0x0e, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    ] + [0] * (KEY_PIXEL_WIDTH * KEY_PIXEL_HEIGHT * 3)


What I am thinking to have is just 72x72 8 bit image colours coming from LUT, 256 colours are enough for this display, but need to build the routine to sent the image. There is need to dynamically update the image several times in second (when there is updates)
 
Last edited:
[0] * (KEY_PIXEL_WIDTH * KEY_PIXEL_HEIGHT * 3)

Note, I only know enough python to be dangerous and only when I have google available :D.

I think it is saying that there are that many 0 bytes that are appended onto the stuff before it.
 
Thanks I will give it a try, it is just many unsure things, dividing it to 64 byte writes on the two blocks and getting the image bytes on right order, and still a bit unsure if the interface is correctly configured...
 
Now if it were me, and if I had a setup that could run the python code mentioned, I would then tend to hook up
a Logic Analyzer and the like and see exactly what they are sending and receiving.
 
Logic analyser would be nice, I actually new how to use such 30 years ago, but now it would likely be more learning curve than this :) I have been thinking tough getting Wireshark and seeing what it can do with USB.


As it is, still trying to figure this out

- BLANK_KEY_IMAGE header 54 bytes and actual image size 72*72*3 = 15 552 bytes => 15 606
- two packets to send each have 12 byte header
- image_report_payload_length = BLANK_KEY_IMAGE length / 2 = 7803
- bytes_sent for first page 0 for second 7803
- this_length for first page 7803, for second page bytes_remaining

but this is confusing

this_length = min(bytes_remaining, image_report_payload_length)
and at the end

bytes_remaining = bytes_remaining - this_length

Just trying to figure out what is (1 or 0) "1 if this_length == bytes_remaining else 0, " for first and second page



My guess would be

the length for both pages is 8191 bytes

first page: 12 bytes + 54 bytes + how many actual image bytes? + padding for rest
second page: 12 bytes + rest of actual image bytes + padding for rest

So the question is 'how many actual image bytes? ' for the first page. I think I will just put all that fits on first page and all padding on second and see how the image will line up

I will first build those two page arrays and then send them 64 bytes at time. How do I know when next 64 bytes can be sent?
 
I have no idea. They appear to split into two parts, where you send the Load is the bitmap file contents, which has the 54 byte bmp file header plus the pixel data, that they split
into two parts. Not sure why they used floor operation // but probably assume is divisible by 2...
And they send I think 8191 bytes, but I have no idea what that means, for the last packet? do they say it is 64 or 63...

If you are using the USBHIDParser to do the sending and receiving, where you call sendPacket,

The HID code is setup for output to be double buffered. That is it will allow 2 packets to be queued. If you call it and two are already queued, it will return false.

So you can simply loop calling it until it returns true. Probably with a timeout so if something goes wrong, it does not hang forever.
 
I am not sure what the USBHIDParser means, it has 'USBHIDParser hid1(USB_HID);' but not sure if that is correctly set, or what it actually does. In fact it is very much possible I am using wrong pipe to send data. The incoming data is fine

I found in here https://den.dev/blog/reverse-engineering-stream-deck/

"Remember that this is written through a different pipe than the regular outputs, so you need to explicitly rely on SetFeature rather than writing to the output stream."

It is a different model, but likely the same for this (but do not know what that means in practise)

It seems there is hid1 that I use also for sending and rawhid1 but rawhid1.sendPacket(D1_out,17); gives 'class HID_Device' has no member named 'sendPacket' so if this is the one that should be used for sending, how to do the sending or configuration of it?


***ZZZZZZZZZ** Device HID1 fd9: 60 - connected ***
manufacturer: Elgato Systems
product: Stream Deck
Serial: AL01H1A08927

mesg = 22000681
got report descriptor
Found top level collection C0001
find_driver
driver 20004D20
HID_Device Claim: fd9:60 usage: c0001 - Yes
***XXXXXXXXXXXXX** HID Device rawhid1 fd9: 60 - connected ***
manufacturer: Elgato Systems
product: Stream Deck
Serial: AL01H1A08927

The order in here seems to affect what gets selected as HID device, so with this it is STREAMDECK (changed the order for this to test, normally it has been rawhid1)

USBHost USB_HID;
USBHub hub1(USB_HID);
USBHIDParser hid1(USB_HID);
HID_Device STREAMDECK(USB_HID);
RawHIDController rawhid1(USB_HID);

I just do not understand how this stuff is supposed to be configured HID_Device or RawHIDController etc.

This is the complete device info

Code:
USB HID Device Info Program

This Sketch shows information about plugged in HID devices

*** You can control the output by simple character input to Serial ***
R - Turns on or off showing the raw data
C - Toggles showing changed data only on or off
<anything else> - toggles showing the Hid formatted breakdown of the data

USB2 PLL running
 reset waited 6
USBHS_ASYNCLISTADDR = 0
USBHS_PERIODICLISTBASE = 20004000
periodictable = 20004000
port change: 10001803
    connect
  begin reset
port change: 18001205
  port enabled
  end recovery
new_Device: 480 Mbit/sec
new_Pipe
enumeration:
enumeration:
enumeration:
Device Descriptor:
  12 01 00 02 00 00 00 40 D9 0F 60 00 00 01 01 02 03 01 
    VendorID = 0FD9, ProductID = 0060, Version = 0100
    Class/Subclass/Protocol = 0 / 0 / 0
    Number of Configurations = 1
enumeration:
enumeration:
Manufacturer: Elgato Systems
enumeration:
Product: Stream Deck
enumeration:
Serial Number: AL01H1A08927
enumeration:
Config data length = 41
enumeration:
Configuration Descriptor:
  09 02 29 00 01 01 00 E0 C8 
    NumInterfaces = 1
    ConfigurationValue = 1
  09 04 00 00 02 03 00 00 00 
    Interface = 0
    Number of endpoints = 2
    Class/Subclass/Protocol = 3(HID) / 0 / 0
  09 21 11 01 00 01 22 0B 01 
    HID, 1 report descriptor
  07 05 81 03 00 02 01 
    Endpoint = 1 IN
    Type = Interrupt
    Max Size = 512
    Polling Interval = 1
  07 05 02 03 00 02 01 
    Endpoint = 2 OUT
    Type = Interrupt
    Max Size = 512
    Polling Interval = 1
enumeration:
USBHub memory usage = 960
USBHub claim_device this=2000B760
USBHub memory usage = 960
USBHub claim_device this=2000BB20

USBDeviceInfo claim this=2000CAC8

****************************************
** Device Level **
  vid=FD9
  pid=60
  bDeviceClass = 0
  bDeviceSubClass = 0
  bDeviceProtocol = 0
09 04 00 00 02 03 00 00 00 09 21 11 01 00 01 22 0B 01 07 05 81 03 00 02 01 07 05 02 03 00 02 01 
HIDParser claim this=200095A0
HIDParser claim this=20009C60
HIDParser claim this=2000A320
HIDParser claim this=2000A9E0
HIDParser claim this=2000B0A0
Descriptor 4 = INTERFACE

USBDeviceInfo claim this=2000CAC8

****************************************
** Interface Level **
09 04 00 00 02 03 00 00 00 09 21 11 01 00 01 22 0B 01 07 05 81 03 00 02 01 07 05 02 03 00 02 01 
 bInterfaceNumber = 0
 number end points = 2
 bInterfaceClass =    3
 bInterfaceSubClass = 0
    HID
 bInterfaceProtocol = 0
report descriptor size = 267
  endpoint = 81
    attributes = 3 Interrupt
    size = 512
    interval = 1
  endpoint = 2
    attributes = 3 Interrupt
    size = 512
    interval = 1
HIDParser claim this=200095A0
 bInterfaceNumber =   0
 bInterfaceClass =    3
 bInterfaceSubClass = 0
 bInterfaceProtocol = 0
HID Parser Claim: 09 04 00 00 02 03 00 00 00 09 21 11 01 00 01 22 0B 01 07 05 81 03 00 02 01 07 05 02 03 00 02 01 
report descriptor size = 267
Two endpoint HID:
  endpoint = 81
   size = 512
   interval = 1
  endpoint = 2
   size = 512
   interval = 1
new_Pipe
allocate_interrupt_pipe_bandwidth
  ep interval = 1
  interval = 1
 best_bandwidth = 21, at offset = 0
new_Pipe
allocate_interrupt_pipe_bandwidth
  ep interval = 1
  interval = 1
 best_bandwidth = 42, at offset = 0
Descriptor 33 = HID
Descriptor 5 = ENDPOINT
Descriptor 5 = ENDPOINT
*** Device HID1 fd9: 60 - connected ***
  manufacturer: Elgato Systems
  product: Stream Deck
  Serial: AL01H1A08927
control callback (hid)
05 0C 09 01 A1 01 09 01 05 09 19 01 29 10 15 00 26 FF 00 75 08 95 10 85 01 81 02 0A 00 FF 15 00 26 FF 00 75 08 95 10 85 A0 81 02 0A 00 FF 15 00 26 FF 00 75 08 95 10 85 A1 81 02 0A 00 FF 15 00 26 FF 00 75 08 96 FE 1F 85 02 91 02 A1 00 0A 00 FF 15 00 26 FF 00 75 08 95 10 85 03 B1 02 C0 A1 00 0A 00 FF 15 00 26 FF 00 75 08 95 10 85 04 B1 02 C0 A1 00 0A 00 FF 15 00 26 FF 00 75 08 95 10 85 05 B1 02 C0 A1 00 0A 00 FF 15 00 26 FF 00 75 08 95 01 85 06 B1 02 C0 A1 00 0A 00 FF 15 00 26 FF 00 75 08 95 10 85 07 B1 04 C0 A1 00 0A 00 FF 15 00 26 FF 00 75 08 95 01 85 08 B1 04 C0 A1 00 0A 00 FF 15 00 26 FF 00 75 08 95 10 85 09 B1 04 C0 A1 00 0A 00 FF 15 00 26 FF 00 75 08 95 10 85 0A B1 04 C0 A1 00 0A 00 FF 15 00 26 FF 00 75 08 95 10 85 0B B1 04 C0 A1 00 0A 00 FF 15 00 26 FF 00 75 08 95 10 85 A3 B1 04 C0 C0 
  mesg = 22000681
  got report descriptor
Found top level collection C0001
find_driver
  driver 200041E0
HIDDumpController Claim: fd9:60 usage: c0001 - Yes
*** HID Device hdc1 fd9: 60 - connected ***
  manufacturer: Elgato Systems
  product: Stream Deck
  Serial: AL01H1A08927

And this the current experimental code

Code:
#include <USBHost_t36.h>

class HID_Device : public USBHIDInput {
public:
  HID_Device(USBHost &host, uint32_t usage = 0) : fixed_usage_(usage) { init(); }
  uint32_t usage(void) {return usage_;}
  static bool show_raw_data;
  static bool show_formated_data;
  static bool changed_data_only;
  //static void hid_send_raw_data(uint8_t D1_out_[]){RawHIDController::sendPacket(D1_out_);};
  
//protected:
  virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage);
  virtual bool hid_process_in_data(const Transfer_t *transfer);
  virtual bool hid_process_out_data(const Transfer_t *transfer);
  virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax);
  virtual void hid_input_data(uint32_t usage, int32_t value);
  virtual void hid_input_end();
  virtual void disconnect_collection(Device_t *dev);
  
//private:
  void init();
  USBHIDParser *driver_;
  uint8_t collections_claimed = 0;
  volatile int hid_input_begin_level_ = 0;
  uint32_t fixed_usage_;
  uint32_t usage_ = 0;
  // Track changing fields. 
  const static int MAX_CHANGE_TRACKED = 512;
  uint32_t usages_[MAX_CHANGE_TRACKED];
  int32_t values_[MAX_CHANGE_TRACKED];
  int count_usages_ = 0;
  int index_usages_ = 0;
  
  // See if we can contribute transfers
  Transfer_t mytransfers[2] __attribute__ ((aligned(32)));
};



USBHost USB_HID;
USBHub hub1(USB_HID);
USBHIDParser hid1(USB_HID);
RawHIDController rawhid1(USB_HID);

HID_Device STREAMDECK(USB_HID);


USBDriver *drivers[] = {&hub1, &hid1};
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))
const char * driver_names[CNT_DEVICES] = {"Hub1",  "HID1" };
bool driver_active[CNT_DEVICES] = {false, false};

// Lets also look at HID Input devices
USBHIDInput *hiddrivers[] = {&STREAMDECK,  &rawhid1};
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))
const char * hid_driver_names[CNT_DEVICES] = {"STREAMDECK", "rawhid1" };
bool hid_driver_active[CNT_DEVICES] = {false, false};
bool show_changed_only = false;

uint8_t D1_out[512]; 



    
    uint8_t BLANK_KEY_IMAGE_START[] = {
        0x42, 0x4d, 0xf6, 0x3c, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00,
        0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x48, 0x00,
        0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00,
        0x00, 0x00, 0xc0, 0x3c, 0x00, 0x00, 0xc4, 0x0e,
        0x00, 0x00, 0xc4, 0x0e, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } ; 
    

    uint8_t HEADER_PAGE_1[16];
    uint8_t HEADER_PAGE_2[16];
    uint8_t IMG_1ST_HALF[8200];
    uint8_t IMG_2ND_HALF[8200];
    uint8_t key = 5;
    uint8_t DATA_OUT[65];
    uint8_t Bsend;

 
void setup()
{

delay(2000);
Serial.println("*START");
    HEADER_PAGE_1[0] = 0x02;    HEADER_PAGE_2[0] = 0x02;
    HEADER_PAGE_1[1] = 0x01;    HEADER_PAGE_2[1] = 0x01;
    HEADER_PAGE_1[2] = 0x01;    HEADER_PAGE_2[2] = 0x02; //page number

    HEADER_PAGE_1[4] = 0x00;    HEADER_PAGE_2[4] = 0x01; // 1 if this_length == bytes_remaining else 0,
    HEADER_PAGE_1[5] = key+1;   HEADER_PAGE_2[5] = key+1; // the key to put the picture on

    for (uint16_t i = 0;  i < 16;   i++) {IMG_1ST_HALF[i]= HEADER_PAGE_1[i]; }
    for (uint16_t i = 16; i < 70;   i++) {IMG_1ST_HALF[i]= BLANK_KEY_IMAGE_START[i-16]; }
    for (uint16_t i = 70; i < 7846; i++) {IMG_1ST_HALF[i]= 30; }//data for image area

    for (uint16_t i = 0;  i < 16;   i++) {IMG_2ND_HALF[i]= HEADER_PAGE_2[i]; }
    for (uint16_t i = 16; i < 7792; i++) {IMG_2ND_HALF[i]= 60; }//data for image area

   
  


  USB_HID.begin();
 // rawhid1.attachReceive(OnReceiveHidData);

  
}


void loop()
{
  USB_HID.Task();



  for (uint8_t i = 0; i < CNT_DEVICES; i++) {
    if (*drivers[i] != driver_active[i]) {
      if (driver_active[i]) {
        Serial.printf("*** Device % s - disconnected ***\n", driver_names[i]);
        driver_active[i] = false;
      } else {
        Serial.printf("*** Device % s % x: % x - connected ***\n", driver_names[i], drivers[i]->idVendor(), drivers[i]->idProduct());
        driver_active[i] = true;

        const uint8_t *psz = drivers[i]->manufacturer();
        if (psz && *psz) Serial.printf("  manufacturer: % s\n", psz);
        psz = drivers[i]->product();
        if (psz && *psz) Serial.printf("  product: % s\n", psz);
        psz = drivers[i]->serialNumber();
        if (psz && *psz) Serial.printf("  Serial: % s\n", psz);
      }
    }
  }

  for (uint8_t i = 0; i < CNT_HIDDEVICES; i++) {
    if (*hiddrivers[i] != hid_driver_active[i]) {
      if (hid_driver_active[i]) {
        Serial.printf("*** HID Device % s - disconnected ***\n", hid_driver_names[i]);
        hid_driver_active[i] = false;
      } else {
        Serial.printf("*** HID Device % s % x: % x - connected ***\n", hid_driver_names[i], hiddrivers[i]->idVendor(), hiddrivers[i]->idProduct());
        hid_driver_active[i] = true;

        const uint8_t *psz = hiddrivers[i]->manufacturer();
        if (psz && *psz) Serial.printf("  manufacturer: % s\n", psz);
        psz = hiddrivers[i]->product();
        if (psz && *psz) Serial.printf("  product: % s\n", psz);
        psz = hiddrivers[i]->serialNumber();
        if (psz && *psz) Serial.printf("  Serial: % s\n", psz);
      }
    }
  }
}



bool HID_Device::show_raw_data = true;
bool HID_Device::show_formated_data = true;
bool HID_Device::changed_data_only = false;

void HID_Device::init()
{
  USBHost::contribute_Transfers(mytransfers, sizeof(mytransfers) / sizeof(Transfer_t));
  USBHIDParser::driver_ready_for_hid_collection(this);
}

hidclaim_t HID_Device::claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage)
{
  // only claim RAWHID devices currently: 16c0:0486
  Serial.printf("HID_Device Claim: %x:%x usage: %x", dev->idVendor, dev->idProduct, topusage);
  if (mydevice != NULL && dev != mydevice) {
    Serial.println("- NO (Device)");
    return CLAIM_NO;
  }
  if (usage_ && (usage_ != topusage)) {
    Serial.printf(" - NO (Usage: %x)\n");
    return CLAIM_NO;      // Only claim one
  }
  mydevice = dev;
  collections_claimed++;
  usage_ = topusage;
  driver_ = driver; // remember the driver.
  Serial.println(" - Yes");
  return CLAIM_INTERFACE;  // We want
}

void HID_Device::disconnect_collection(Device_t *dev)
{
  if (--collections_claimed == 0) {
    mydevice = NULL;
    usage_ = 0;
  }
}

void dump_hexbytes(const void *ptr, uint32_t len)
{
  if (ptr == NULL || len == 0) return;
  uint32_t count = 0;
//  if (len > 64) len = 64; // don't go off deep end...
  const uint8_t *p = (const uint8_t *)ptr;
  while (len--) {
    if (*p < 16) Serial.print('0');
    Serial.print(*p++, HEX);
    count++;
    if (((count & 0x1f) == 0) && len) Serial.print("\n");
    else Serial.print(' ');
  } 
  Serial.println();
}

void dump_bytes(const void *ptr, uint32_t len)
{
  if (ptr == NULL || len == 0) return;
  uint32_t count = 0;

  const uint8_t *p = (const uint8_t *)ptr;
  while (len--) {
   // if (*p < 16) Serial.print('0');
    Serial.write(*p++);
    count++;
    if (((count & 0x1f) == 0) && len) Serial.print("\n");
    else Serial.print(' ');
  } 
  Serial.println();
}
bool HID_Device::hid_process_in_data(const Transfer_t *transfer)
{
  // return true if we are not showing formated data...
  hid_input_begin_level_ = 0; // always make sure we reset to 0
  count_usages_ = index_usages_;  // remember how many we output for this one
  index_usages_ = 0;        // reset the index back to zero

  Serial.printf("HID(%x)", usage_);
  if (show_raw_data) {
    Serial.print(": ");
    dump_hexbytes(transfer->buffer, transfer->length);
  }
  else Serial.println();

  return !show_formated_data;
}

bool HID_Device::hid_process_out_data(const Transfer_t *transfer)
{
  Serial.printf("HID_Device::hid_process_out_data: %x\n", usage_);
dump_hexbytes(transfer->buffer, transfer->length);

  return true;
}

void indent_level(int level) {
  if ((level > 5) || (level < 0)) return; // bail if something is off...
  while (level--) Serial.print("  ");
}

void HID_Device::hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax)
{
  // Lets do simplified data for changed only
  if (changed_data_only) return;

  indent_level(hid_input_begin_level_);
  Serial.printf("Begin topusage:%x type:%x min:%d max:%d\n", topusage, type, lgmin, lgmax);
  if (hid_input_begin_level_ < 2)
    hid_input_begin_level_++;
}

void HID_Device::hid_input_data(uint32_t usage, int32_t value)
{

  bool output_data = !changed_data_only;

  // See if something changed.
  if (index_usages_ < count_usages_) {
    if ((usage != usages_[index_usages_]) || (value != values_[index_usages_])) {
      output_data = true;
    }
  } else {
    output_data = true;
  }
  if (index_usages_ < MAX_CHANGE_TRACKED) {
    usages_[index_usages_] = usage;
    values_[index_usages_] = value;
    index_usages_++;
  }

  if (output_data) {
    indent_level(hid_input_begin_level_);
    Serial.printf("usage=%X, value=%d", usage, value);
    if ((value >= ' ') && (value <= '~')) Serial.printf(":%c", value);

    // maybe print out some information about some of the Usage numbers that we know about
    // The information comes from the USB document, HID Usage Tables 
    // https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf
    
    uint16_t usage_page = usage >> 16;
    usage = usage & 0xffff; // keep the lower part
    switch (usage_page) {

      case 9: // Button
        Serial.printf(" (BUTTON %d)", usage);
       
        if(usage == 1 && value == 1){
          
        //Stream deck dim display comand bright
           for (uint8_t i = 0; i < 17; i++) {D1_out[i]=0x00;}
           D1_out[0]=0x05;
           D1_out[1]=0x55;
           D1_out[2]=0xaa;
           D1_out[3]=0xd1;
           D1_out[4]=0x01;
           D1_out[5]=0x60;
   
           hid1.sendPacket(D1_out,17); 
           
        }

           if(usage == 2 && value == 1){
          
        //Stream deck dim display comand dim
           for (uint8_t i = 0; i < 17; i++) {D1_out[i]=0x00;}
           D1_out[0]=0x05;
           D1_out[1]=0x55;
           D1_out[2]=0xaa;
           D1_out[3]=0xd1;
           D1_out[4]=0x01;
           D1_out[5]=0x40;
   
           hid1.sendPacket(D1_out,17); 

           }

          if(usage == 3 && value == 1){
          
        //0x03 SERIAL
           for (uint8_t i = 0; i < 17; i++) {D1_out[i]=0x00;}
           
           D1_out[0]=0x03;

   
           hid1.sendPacket(D1_out,17); 
           
        }

         if(usage == 4 && value == 1){
          
        //Stream deck 0x0B RESET
           for (uint8_t i = 0; i < 17; i++) {D1_out[i]=0x00;}
           
           D1_out[0]=0x0B;
           D1_out[1]=0x63;

   
           hid1.sendPacket(D1_out,17); 
           
           
        }

            if(usage == 5 && value == 1){
          
        //Draw image
        Serial.println();
           for (uint16_t n = 0; n < 128; n++) {
            if(n<127) Bsend = 64; else Bsend = 63;
            for (uint16_t i = n*64;  i < n*64+Bsend;   i++) {DATA_OUT[i] = IMG_1ST_HALF[i]; }
            
            hid1.sendPacket(DATA_OUT,Bsend);
            //delay(1); 
             Serial.print(n);Serial.print(", ");
            }
            Serial.println();
           for (uint16_t n = 0; n < 128; n++) {
            if(n<127) Bsend = 64; else Bsend = 63;
            for (uint16_t i = n*64;  i < n*64+Bsend;   i++) {DATA_OUT[i] = IMG_2ND_HALF[i]; }
            
            hid1.sendPacket(DATA_OUT,Bsend);
            //delay(1); 
            Serial.print(n);Serial.print(", ");
            }
            Serial.println();
           
           
           
        }
        
        break;

        default: 
        Serial.println(" UNKNOWN");
        break;
     
    }
    Serial.println();
  }
}

void HID_Device::hid_input_end()
{
  // Lets do simplified data for changed only
  if (changed_data_only) return;
  hid_input_begin_level_--;
  indent_level(hid_input_begin_level_);
  Serial.println("END:");
}



extern void dump_hexbytes(const void *ptr, uint32_t len);

extern void dump_bytes(const void *ptr, uint32_t len);

static void println(const char *title, uint32_t val, uint8_t b = DEC) {
  Serial.print(title);
  Serial.println(val, b); 
}

static void print(const char *title, uint32_t val, uint8_t b = DEC) {
  Serial.print(title);
  Serial.print(val, b); 
}






bool OnReceiveHidData(uint32_t usage, const uint8_t *data, uint32_t len) {
  // Called for maybe both HIDS for rawhid basic test.  One is for the Teensy
  // to output to Serial. while still having Raw Hid...
  if (usage == 0xc0001) {
    // Lets trim off trailing null characters.
    while ((len > 0) && (data[len - 1] == 0)) {
      len--;
    }
    if (len) {
      Serial.print("RawHid Serial: ");
      Serial.write(data, len);
    }
  } else {
    Serial.print("RawHID data: ");
    Serial.println(usage, HEX);
    while (len) {
      uint8_t cb = (len > 16) ? 16 : len;
      const uint8_t *p = data;
      uint8_t i;
      for (i = 0; i < cb; i++) {
        Serial.printf("%02x ", *p++);
      }
      Serial.print(": ");
      for (i = 0; i < cb; i++) {
        Serial.write(((*data >= ' ') && (*data <= '~')) ? *data : '.');
        data++;
      }
      len -= cb;
      Serial.println();
    }
  }

  return true;
}

Reading the buttons works ok, sending some data* just removes the default logo from the screen, but has no other effect. *on buttons 1 to 5, search 'case 9: // Button'
Trying to send the image 'if(usage == 5 && value == 1){'causes restart.



The USB Host initialisation is pure copying from example and guessing. could very well be not set correctly for sending data?
 
Last edited:
Back
Top