Adding a new USB type

Status
Not open for further replies.

lhagan

Member
I'm trying to add a new USB type that combines HID & Raw HID. I think I've worked most of it out from the instructions in "usb_desc.h", but all I get are "XYZ was not declared in this scope" errors when I try to use my new type.

In an attempt to narrow down the problem, I also tried just making an exact duplicate of Raw HID with a different name, and that doesn't work either.

In "boards.txt", I've added:
Code:
teensy3.menu.usb.rawhid2.name=Raw HID 2
teensy3.menu.usb.rawhid2.build.define0=-DUSB_RAWHID2
teensy3.menu.usb.rawhid2.fake_serial=teensy_gateway

..in "usb_desc.h":
Code:
#elif defined(USB_RAWHID2)
  #define VENDOR_ID		0x16C0
  #define PRODUCT_ID		0x0486
  #define RAWHID_USAGE_PAGE	0xFFAB  // recommended: 0xFF00 to 0xFFFF
  #define RAWHID_USAGE		0x0200  // recommended: 0x0100 to 0xFFFF
  #define MANUFACTURER_NAME	{'T','e','e','n','s','y','d','u','i','n','o'}
  #define MANUFACTURER_NAME_LEN	11
  #define PRODUCT_NAME		{'T','e','e','n','s','y','d','u','i','n','o',' ','R','a','w','H','I','D'}
  #define PRODUCT_NAME_LEN	18
  #define EP0_SIZE		64
  #define NUM_ENDPOINTS         6
  #define NUM_USB_BUFFERS	12
  #define NUM_INTERFACE		2
  #define RAWHID_INTERFACE      0	// RawHID
  #define RAWHID_TX_ENDPOINT    3
  #define RAWHID_TX_SIZE        64
  #define RAWHID_TX_INTERVAL    1
  #define RAWHID_RX_ENDPOINT    4
  #define RAWHID_RX_SIZE        64
  #define RAWHID_RX_INTERVAL    1
  #define SEREMU_INTERFACE      1	// Serial emulation
  #define SEREMU_TX_ENDPOINT    1
  #define SEREMU_TX_SIZE        64
  #define SEREMU_TX_INTERVAL    1
  #define SEREMU_RX_ENDPOINT    2
  #define SEREMU_RX_SIZE        32
  #define SEREMU_RX_INTERVAL    2
  #define RAWHID_DESC_OFFSET	(9 + 9)
  #define SEREMU_DESC_OFFSET	(9 + 9+9+7+7 + 9)
  #define CONFIG_DESC_SIZE	(9 + 9+9+7+7 + 9+9+7+7)
  #define ENDPOINT1_CONFIG	ENDPOINT_TRANSIMIT_ONLY
  #define ENDPOINT2_CONFIG	ENDPOINT_RECEIVE_ONLY
  #define ENDPOINT3_CONFIG	ENDPOINT_TRANSIMIT_ONLY
  #define ENDPOINT4_CONFIG	ENDPOINT_RECEIVE_ONLY

...and in "usb_inst.cpp":
Code:
#ifdef USB_RAWHID2
usb_rawhid_class RawHID;
usb_seremu_class Serial;
#endif

Here's the full error message when trying to build the Raw HID Basic example code:
Code:
/Users/lhagan/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/arm-none-eabi/bin/arm-none-eabi-g++ -c -g -Os -Wall -fno-exceptions -ffunction-sections -fdata-sections -mcpu=cortex-m4 -DF_CPU=96000000 -MMD -DUSB_VID=null -DUSB_PID=null -DARDUINO=105 -mthumb -nostdlib -D__MK20DX128__ -DTEENSYDUINO=118 -fno-rtti -felide-constructors -std=gnu++0x -DUSB_RAWHID2 -DLAYOUT_US_ENGLISH -I/Users/lhagan/Applications/Arduino.app/Contents/Resources/Java/hardware/teensy/cores/teensy3 /var/folders/t4/vmhr2xd16439ttrwnpb54_j00000gn/T/build2754205012057077850.tmp/Basic.cpp -o /var/folders/t4/vmhr2xd16439ttrwnpb54_j00000gn/T/build2754205012057077850.tmp/Basic.cpp.o 
Basic.pde: In function 'void setup()':
Basic:15: error: 'Serial' was not declared in this scope
Basic.pde: In function 'void loop()':
Basic:29: error: 'RawHID' was not declared in this scope
Basic:34: error: 'Serial' was not declared in this scope
Basic:63: error: 'Serial' was not declared in this scope
Basic:67: error: 'Serial' was not declared in this scope

Of course, the example code builds just fine if I select "Raw HID" instead of "Raw HID 2". I feel like I must be missing something really simple, but I just can't seem to find it. Any suggestions would be much appreciated!

Setup: Arduino 1.0.5 & Teensyduino 1.19 on a Mac, Teensy 3
 
OK, so no excuse for missing a previous post on the exact same issue: http://forum.pjrc.com/threads/24431...s-USB-Serial-HID?p=36841&viewfull=1#post36841

However, that wasn't enough to get everything working, so I thought it'd help to provide a complete step-by-step example to hopefully save others some time in the future.

In "usb_desc.h", copy an existing type that contains one or more of the USB types you want. Then, start modifying it as needed:

  1. Customize PRODUCT_NAME to whatever you want.
  2. Change PRODUCT_NAME_LEN to match the number of characters in your new PRODUCT_NAME.
  3. Copy the relevant lines from another USB type. For example, to add Keyboard, I copied the following from the USB_HID type:
    Code:
      #define KEYBOARD_INTERFACE    0	// Keyboard
      #define KEYBOARD_ENDPOINT     3
      #define KEYBOARD_SIZE         8
      #define KEYBOARD_INTERVAL     1
  4. Edit the XYZ_INTERFACE number. There's one for each interface, it starts at 0, and needs to be unique for each interface. In the Raw HID USB type, RAWHID_INTERFACE is 0 and SEREMU_INTERFACE is 1, so I made KEYBOARD_INTERFACE 2. It's simplest to just add your new interface(s) to the end.
  5. Copy in the XYZ_DESC_OFFSET line from the other USB type and edit to the correct value. XYZ_DESC_OFFSET represents a size, always starts and ends with 9, and has the sum of the bLengths for all interfaces that precede it the middle. For example, the offset of interface2 would be (9 + interface0_length + interface1_length + 9). You can find the bLengths in usb_desc.c, but I've compiled them here for reference:
    Code:
    keyboard    9+9+7
    mouse       9+9+7
    seremu      9+9+7+7
    joystick    9+9+7
    flightsim   9+9+7+7
    rawhid      9+9+7+7
    midi        9+7+6+6+9+9+9+5+9+5
    serial      9+5+5+4+5+7+9+7+7
    In this example, RAWHID is the first interface, so RAWHID_DESC_OFFSET is just (9 + 9). SEREMU is the next interface, so its OFFSET adds the bLengths from the previous interface (RAWHID), thus SEREMU_DESC_OFFSET is (9 + 9+9+7+7 + 9). Keyboard is the last interface, so KEYBOARD_DESC_OFFSET has the bLengths from both RAWHID and SEREMU: (9 + 9+9+7+7 + 9+9+7+7 + 9)
  6. Edit the CONFIG_DESC_SIZE. The approach here is similar to the XYZ_OFFSETs above, it does not have an extra 9 at the end. It starts with a 9 and includes the bLengths for all interfaces. In this example, it's (9 + rawhid_length + seremu_length + keyboard_length), or (9 + 9+9+7+7 + 9+9+7+7 + 9+9+7)
  7. Add endpoints if needed. The original Raw USB type has 4 endpoints, one transmit (TX) and one receive (RX) each for RAWHID and SEREMU. Since I've added KEYBOARD, a 5th endpoint is needed (just a one TX as there is no RX for keyboard).
    Code:
    #define ENDPOINT5_CONFIG	ENDPOINT_TRANSIMIT_ONLY
  8. Update the NUM_ENDPOINTS to match the number of endpoints you have (i.e. 5 in this case).
  9. Modify the endpoint number(s) for your new interface(s) to match the endpoint(s) added above, i.e.
    Code:
      #define KEYBOARD_ENDPOINT     5
  10. Increase the NUM_USB_BUFFERS. I'm not actually sure if/when this is necessary, but this value varies from type to type. I made mine equal to the highest value (30). If anyone can provide some better guidance here, I'd love to update these instructions to be more accurate.

Here's the full example from my usb_desc.h:
Code:
#elif defined(USB_KEYBOARD_RAWHID)
  #define VENDOR_ID             0x16C0
  #define PRODUCT_ID            0x0486
  #define RAWHID_USAGE_PAGE     0xFFAB  // recommended: 0xFF00 to 0xFFFF
  #define RAWHID_USAGE          0x0200  // recommended: 0x0100 to 0xFFFF
  #define MANUFACTURER_NAME     {'T','e','e','n','s','y','d','u','i','n','o'}
  #define MANUFACTURER_NAME_LEN	11
  #define PRODUCT_NAME          {'K','e','y','b','o','a','r','d','/','R','a','w','H','I','D'}
  #define PRODUCT_NAME_LEN	    15
  #define EP0_SIZE		        64
  #define NUM_ENDPOINTS         5
  #define NUM_USB_BUFFERS	    30
  #define NUM_INTERFACE		    3
  #define RAWHID_INTERFACE      0	// RawHID
  #define RAWHID_TX_ENDPOINT    3
  #define RAWHID_TX_SIZE        64
  #define RAWHID_TX_INTERVAL    1
  #define RAWHID_RX_ENDPOINT    4
  #define RAWHID_RX_SIZE        64
  #define RAWHID_RX_INTERVAL    1
  #define SEREMU_INTERFACE      1	// Serial emulation
  #define SEREMU_TX_ENDPOINT    1
  #define SEREMU_TX_SIZE        64
  #define SEREMU_TX_INTERVAL    1
  #define SEREMU_RX_ENDPOINT    2
  #define SEREMU_RX_SIZE        32
  #define SEREMU_RX_INTERVAL    2
  #define KEYBOARD_INTERFACE    2	// Keyboard
  #define KEYBOARD_ENDPOINT     5
  #define KEYBOARD_SIZE         8
  #define KEYBOARD_INTERVAL     1
  #define RAWHID_DESC_OFFSET    (9 + 9)
  #define SEREMU_DESC_OFFSET    (9 + 9+9+7+7 + 9)
  #define KEYBOARD_DESC_OFFSET  (9 + 9+9+7+7 + 9+9+7+7 + 9)
  #define CONFIG_DESC_SIZE      (9 + 9+9+7+7 + 9+9+7+7 + 9+9+7)
  #define ENDPOINT1_CONFIG	ENDPOINT_TRANSIMIT_ONLY
  #define ENDPOINT2_CONFIG	ENDPOINT_RECEIVE_ONLY
  #define ENDPOINT3_CONFIG	ENDPOINT_TRANSIMIT_ONLY
  #define ENDPOINT4_CONFIG	ENDPOINT_RECEIVE_ONLY
  #define ENDPOINT5_CONFIG	ENDPOINT_TRANSIMIT_ONLY

In "usb_inst.cpp", add a conditional statement to create instances of the C++ objects for your new type:
Code:
#ifdef USB_KEYBOARD_RAWHID
usb_keyboard_class Keyboard;
usb_rawhid_class RawHID;
usb_seremu_class Serial;
#endif

So you can select your new USB type from the Arduino Tools menu, add it to "boards.txt":
Code:
teensy3.menu.usb.keyboardrawhid.name=Keyboard + Raw HID
teensy3.menu.usb.keyboardrawhid.build.define0=-DUSB_KEYBOARD_RAWHID
teensy3.menu.usb.keyboardrawhid.fake_serial=teensy_gateway
The teensy3..define0 value needs to match the #ifdef you put in "usb_inst.cpp".

At this point, you're almost ready to build, but you'll get a bunch of "XYZ was not declared in this scope" errors. To fix this, you need to add your new USB type to the "#if defined"s of the classes you're using (in "usb_xyz.h"). In this example, we need rawhid, seremu, and keyboard:

In "usb_rawhid.h":
Code:
#if defined(USB_RAWHID) || defined(USB_KEYBOARD_RAWHID)

In "usb_seremu.h":
Code:
#if defined(USB_HID) || defined(USB_MIDI) || defined(USB_RAWHID) || defined(USB_FLIGHTSIM) || defined(USB_KEYBOARD_RAWHID)

In "usb_keyboard.h":
Code:
#if defined(USB_HID) || defined(USB_SERIAL_HID) || defined(USB_KEYBOARD_RAWHID)

That should do it!
 
Hi, thanks for this. I just got back to my project and had completely forgotten how I achieved this. I had deferred the RAW HID interface and lost all my updates later when I upgraded Arduino and Teensyduino. It was an uphill struggle the first time, this time you made it easy for me :0)

thanks
Ex
 
I tried to do the same, but adding Serial to 'keyboard mouse and touchscreen'

in boards.txt:
Code:
teensy31.menu.usb.hidtouch=Keyboard + Mouse + TouchScreen + Serial
teensy31.menu.usb.serialhid.build.usbtype=USB_HID_TOUCHSCREEN_SERIAL

in usb_desc.h:
Code:
  #elif defined(USB_HID_TOUCHSCREEN_SERIAL)
  #define VENDOR_ID		0x16C0
  #define PRODUCT_ID		0x0481
  #define MANUFACTURER_NAME	{'T','e','e','n','s','y','d','u','i','n','o'}
  #define MANUFACTURER_NAME_LEN	11
  #define PRODUCT_NAME		{'S','e','r','i','a','l','/','K','e','y','b','o','a','r','d','/','M','o','u','s','e','/','T','o','u','c','h','s','c','r','e','e','n'}
  #define PRODUCT_NAME_LEN		33
  #define EP0_SIZE				64
  #define NUM_ENDPOINTS         6
  #define NUM_USB_BUFFERS		20
  #define NUM_INTERFACE			5
  #define CDC_DATA_INTERFACE	2	// Serial
  #define CDC_ACM_ENDPOINT		2
  #define CDC_RX_ENDPOINT       3
  #define CDC_TX_ENDPOINT       4
  #define CDC_ACM_SIZE          16
  #define CDC_RX_SIZE           64
  #define CDC_TX_SIZE           64
  #define KEYBOARD_INTERFACE    0	// Keyboard
  #define KEYBOARD_ENDPOINT     3
  #define KEYBOARD_SIZE         8
  #define KEYBOARD_INTERVAL     1
  #define KEYMEDIA_INTERFACE    3	// Keyboard Media Keys
  #define KEYMEDIA_ENDPOINT     4
  #define KEYMEDIA_SIZE         8
  #define KEYMEDIA_INTERVAL     4
  #define MOUSE_INTERFACE       1	// Mouse
  #define MOUSE_ENDPOINT        6
  #define MOUSE_SIZE            8
  #define MOUSE_INTERVAL        2
  #define MULTITOUCH_INTERFACE  4	// Touchscreen
  #define MULTITOUCH_ENDPOINT   5
  #define MULTITOUCH_SIZE       9
  #define MULTITOUCH_FINGERS    10
  #define ENDPOINT1_CONFIG	ENDPOINT_TRANSIMIT_ONLY
  #define ENDPOINT2_CONFIG	ENDPOINT_RECEIVE_ONLY
  #define ENDPOINT3_CONFIG	ENDPOINT_TRANSIMIT_ONLY
  #define ENDPOINT4_CONFIG	ENDPOINT_TRANSIMIT_ONLY
  #define ENDPOINT5_CONFIG	ENDPOINT_TRANSIMIT_ONLY
  #define ENDPOINT6_CONFIG	ENDPOINT_TRANSIMIT_ONLY
  #define ENDPOINT7_CONFIG	ENDPOINT_TRANSIMIT_ONLY

in usb_inst.cpp:
Code:
#ifdef USB_HID_TOUCHSCREEN_SERIAL
usb_keyboard_class Keyboard;
usb_mouse_class Mouse;
usb_serial_class Serial;
usb_touch_class Touch;
#endif

It shows in the menu, compiles and uploads, but Serial is not added, it keeps showing 'emulated serial'...

In my application I send coordinates to a Teensy3.2 over Serial and then let it emulate touch events. This works when I select 'All of the above' as usb type, but then my sound is gone :)
 
Last edited:
I tried to do the same, but adding Serial to 'keyboard mouse and touchscreen'
...
Your endpoint config is completely wrong.

#define NUM_ENDPOINTS 6

but later you have 7.

#define CDC_RX_ENDPOINT 3
...
#define KEYBOARD_ENDPOINT 3

They must be unique.

#define CDC_RX_ENDPOINT 3
#define ENDPOINT3_CONFIG ENDPOINT_TRANSIMIT_ONLY

You can't receive on a transmit endpoint.
 
this should be better:

Code:
  #elif defined(USB_HID_TOUCHSCREEN_SERIAL)
  #define VENDOR_ID		0x16C0
  #define PRODUCT_ID		0x0481
  #define MANUFACTURER_NAME	{'T','e','e','n','s','y','d','u','i','n','o'}
  #define MANUFACTURER_NAME_LEN	11
  #define PRODUCT_NAME		{'S','e','r','i','a','l','/','K','e','y','b','o','a','r','d','/','M','o','u','s','e','/','T','o','u','c','h','s','c','r','e','e','n'}
  #define PRODUCT_NAME_LEN		33
  #define EP0_SIZE				64
  #define NUM_ENDPOINTS			7
  #define NUM_USB_BUFFERS		30
  #define NUM_INTERFACE			6
  #define CDC_IAD_DESCRIPTOR	1
  #define CDC_STATUS_INTERFACE	0
  #define CDC_DATA_INTERFACE	1	// Serial
  #define CDC_ACM_ENDPOINT		2
  #define CDC_RX_ENDPOINT       3
  #define CDC_TX_ENDPOINT       4
  #define CDC_ACM_SIZE          16
  #define CDC_RX_SIZE           64
  #define CDC_TX_SIZE           64
  #define KEYBOARD_INTERFACE    2	// Keyboard
  #define KEYBOARD_ENDPOINT     1
  #define KEYBOARD_SIZE         8
  #define KEYBOARD_INTERVAL     1
  #define KEYMEDIA_INTERFACE    5	// Keyboard Media Keys
  #define KEYMEDIA_ENDPOINT     7
  #define KEYMEDIA_SIZE         8
  #define KEYMEDIA_INTERVAL     4
  #define MOUSE_INTERFACE       3	// Mouse
  #define MOUSE_ENDPOINT        5
  #define MOUSE_SIZE            8
  #define MOUSE_INTERVAL        2
  #define MULTITOUCH_INTERFACE  4	// Touchscreen
  #define MULTITOUCH_ENDPOINT   6
  #define MULTITOUCH_SIZE       9
  #define MULTITOUCH_FINGERS    10
  #define ENDPOINT1_CONFIG	ENDPOINT_TRANSIMIT_ONLY
  #define ENDPOINT2_CONFIG	ENDPOINT_TRANSIMIT_ONLY
  #define ENDPOINT3_CONFIG	ENDPOINT_RECEIVE_ONLY
  #define ENDPOINT4_CONFIG	ENDPOINT_TRANSIMIT_ONLY
  #define ENDPOINT5_CONFIG	ENDPOINT_TRANSIMIT_ONLY
  #define ENDPOINT6_CONFIG	ENDPOINT_TRANSIMIT_ONLY
  #define ENDPOINT7_CONFIG	ENDPOINT_TRANSIMIT_ONLY

but this still gives me emulated serial, where do i add this?
 
Last edited:
It shows in the menu, compiles and uploads, but Serial is not added, it keeps showing 'emulated serial'...

The "fake_serial=teensy_gateway" key in the menu lines of boards.txt controls this. Find the group of menu lines for your new USB type and delete the fake_serial line. This line also controls whether the serial monitor tries to access Teensy as a real serial device, or through a localhost connection to the HID gateway program.
 
Status
Not open for further replies.
Back
Top