Spark Logic

USB Composite (HID / MIDI / Mass Storage) library

arpruss Sun Dec 03, 2017 8:32 pm

I’ve worked hard to bring the addMidiHID branch of the libmaple-based core up to date, and even made a pull request to merge that into the main branch, but then today I realized that it would be simpler for maintenance and customization to put all the stuff into a separate library, making use of the fact that the core’s USB serial support has __weak references where it matters.

So, here is a USB library compatible with the main libmaple-based core:

(The library is also bundled with Roger’s core.)

As of February 2018, the library supports:

  • USB HID
  • Serial (use CompositeSerial, not Serial)
  • USB MIDI
  • XBox 360 controller
  • Mass storage

You can do various composite combinations, e.g., HID+Serial, Mass storage+HID, etc.

Here’s some example code: an adapter from the IBM PC game port to USB joystick:

#include <GameControllers.h> // https://github.com/arpruss/GameControllersSTM32
#include <USBComposite.h>

USBHID HID; // declare HID plugin instance
HIDJoystick Joystick(HID); // declare Joystick profile instance

GamePortController controller(PA0,PA1,PA2,PA3,PA4,PA5,PA6,PA7);

void setup()
{
// to composite serial, use USBHID_begin_with_serial() instead
USBHID.begin(HID_JOYSTICK); // other options: HID_KEYBOARD, HID_MOUSE, HID_KEYBOARD_JOYSTICK, HID_KEYBOARD_MOUSE, HID_KEYBOARD_MOUSE_JOYSTICK

pinMode(LED_BUILTIN, OUTPUT);

digitalWrite(LED_BUILTIN, 1);
controller.begin();
Joystick.setManualReportMode(true); // aggregate data before sending
}

void loop()
{
GameControllerData_t data;
if (controller.read(&data)) {
Joystick.X(data.joystickX);
Joystick.Y(data.joystickY);
Joystick.Xrotate(data.cX);
Joystick.sliderRight(data.shoulderRight);
uint8_t mask = 1;
for (int i=1; i<=8; i++, mask <<= 1)
Joystick.button(i, (data.buttons & mask) != 0);
Joystick.send();
}

delay(10);
}

Replies

arpruss Tue Dec 12, 2017 10:57 pm

I’ve been trying this with an Ubuntu VM on Windows. It works when I turn off the serial in the code (#undef SERIAL_COMPOSITE in composite_usb.h in the current code version), but with serial AND keyboard, it fails.

arpruss Wed Dec 13, 2017 1:04 am

Now it works under Android and in the Ubuntu VM. The problem was in a descriptor I grabbed from the libarra111 fork.

mrburnette Wed Dec 13, 2017 3:41 pm

[arpruss – Wed Dec 13, 2017 1:04 am] –
Now it works under Android and in the Ubuntu VM. The problem was in a descriptor I grabbed from the libarra111 fork.

This is indeed interesting.

I played much a long time ago with the AVR-specific V-USB implementation. I also played with the ATmega32U4 “micro.”

Ray

arpruss Wed Dec 13, 2017 4:38 pm

What I do need help with is figuring out how to make HID output (host-to-device) reports work. HID feature reports work fine in both directions.

Of course, it’s not really needed, except maybe for speed and efficiency. Everything you can do with an output report you can do with a feature report, I think.

anv Wed Dec 13, 2017 6:39 pm

[arpruss – Wed Dec 13, 2017 1:04 am] –
Now it works under Android and in the Ubuntu VM. The problem was in a descriptor I grabbed from the libarra111 fork.

Yes!! Keyboard now works under linux. If you need tests or USB info from real joysticks, keyboard, mous or midi devices just ask me. I can copy it for you.
I.E. here is the info from a real usb keyboard:

Kernel information (dmesg) when plugging the device:
[ 579.488436] usb 1-4: new low-speed USB device number 7 using xhci_hcd
[ 579.663196] usb 1-4: New USB device found, idVendor=0461, idProduct=4e29
[ 579.663197] usb 1-4: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 579.663198] usb 1-4: Product: Wired USB Keyboard
[ 579.663199] usb 1-4: Manufacturer: PRIMAX
[ 579.666268] input: PRIMAX Wired USB Keyboard as /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0/0003:0461:4E29.0005/input/input19
[ 579.717495] hid-generic 0003:0461:4E29.0005: input,hidraw4: USB HID v1.10 Keyboard [PRIMAX Wired USB Keyboard] on usb-0000:00:14.0-4/input0
[ 579.723297] input: PRIMAX Wired USB Keyboard as /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.1/0003:0461:4E29.0006/input/input20
[ 579.775720] hid-generic 0003:0461:4E29.0006: input,hiddev0,hidraw5: USB HID v1.10 Device [PRIMAX Wired USB Keyboard] on usb-0000:00:14.0-4/input1

anv Wed Dec 13, 2017 6:59 pm

[arpruss – Wed Dec 13, 2017 4:38 pm] –
What I do need help with is figuring out how to make HID output (host-to-device) reports work. HID feature reports work fine in both directions.

Of course, it’s not really needed, except maybe for speed and efficiency. Everything you can do with an output report you can do with a feature report, I think.

May be you could check the HID-project library https://github.com/NicoHood/HID
It works OK reciving data from the host like keyboard leds. See this example (I tested it with an Arduino Leonardo and works ok):

/*
Copyright (c) 2014-2015 NicoHood
See the readme for credit to other people.

KeyboardLed example

Press a button to toogle caps lock.
Caps lock state is represented by the onboard led.
Leds are only supported on single report HID devices.

See HID Project documentation for more information.
https://github.com/NicoHood/HID/wiki/Keyboard-API
*/

#include "HID-Project.h"

const int pinLed = LED_BUILTIN;
const int pinButton = 2;

void setup() {
pinMode(pinLed, OUTPUT);
pinMode(pinButton, INPUT_PULLUP);

// Sends a clean report to the host. This is important on any Arduino type.
BootKeyboard.begin();
}

void loop() {
// Update Led equal to the caps lock state.
// Keep in mind that on a 16u2 and Arduino Micro HIGH and LOW for TX/RX Leds are inverted.
if (BootKeyboard.getLeds() & LED_CAPS_LOCK)
digitalWrite(pinLed, HIGH);
else
digitalWrite(pinLed, LOW);

// Trigger caps lock manually via button
if (!digitalRead(pinButton)) {
BootKeyboard.write(KEY_CAPS_LOCK);

// Simple debounce
delay(300);
}
}

arpruss Wed Dec 13, 2017 7:32 pm

[anv – Wed Dec 13, 2017 6:39 pm] –
[arpruss – Wed Dec 13, 2017 1:04 am] –
Now it works under Android and in the Ubuntu VM. The problem was in a descriptor I grabbed from the libarra111 fork.

Yes!! Keyboard now works under linux.

But my changes broke feature reports on Windows. I made some more changes and it now seems to work under both Windows and Android. Can you check Linux? (I know Android is Linux, but it might use other USB drivers.)

arpruss Wed Dec 13, 2017 9:28 pm

Output reports now work. I didn’t really have to do anything special once I fixed the descriptors: under the hood, they work just like feature reports. I am learning a bit about USB as I go along.

RogerClark Wed Dec 13, 2017 10:45 pm

Just a thought, but I noticed someone asking about USB Mass storage…

I recall there was some old code from the leaflabs forum, which I tried a long time ago.

I presume that in theory we could add USB mass storage as a library ???

arpruss Thu Dec 14, 2017 1:12 am

One certainly could add it. I don’t want to do it myself, as I don’t have a use case myself.

RogerClark Thu Dec 14, 2017 3:38 am

No worries

If someone else wants it, they can add it

arpruss Sun Dec 17, 2017 3:49 pm

I’ve made a lot of changes to the library over the last couple of days.

1. The publicly useful USB_HID_xxx macros have been renamed to just HID_xxx.
2. The main USBDevice instance should now be referred to as USB instead of HID.
3. Added a consumer control device (volume, pause/play, brightness, etc.). See the consumer example.
4. Improved flow control for host-to-device.

arpruss Mon Dec 18, 2017 3:40 pm

Added serial number support to the library. Default serial number is 00000000000000000001. It can be specified in the USB.begin() call. You can even make it be the device ID, but then you lose any potential usefulness it might have for cryptographic purposes.

RogerClark Mon Dec 18, 2017 8:19 pm

BTW.

i pulled your change for USB power, to my local machine, and then manually checked out the tools files ( from my local master) which had been also changed by your PR, but I think if I merge this back into my local Master (HEAD), it tracks the changes to the tool files as if they were changed twice.

So I think I will need to start again, and manually update the individual USB files, otherwise the git history is confusing as it has those changes to the tools, which are unnecessary

anv Tue Dec 19, 2017 9:41 am

[arpruss – Mon Dec 18, 2017 3:40 pm] –
Added serial number support to the library. Default serial number is 00000000000000000001. It can be specified in the USB.begin() call. You can even make it be the device ID, but then you lose any potential usefulness it might have for cryptographic purposes.

Great job. Is it possible also to assign a text string of the device?

arpruss Tue Dec 19, 2017 2:50 pm

What do you mean?

anv Tue Dec 19, 2017 4:38 pm

[arpruss – Tue Dec 19, 2017 2:50 pm] –
What do you mean?

I’m talking about the ascii strings that describes the devices:

Bus 001 Device 004: ID 0bda:57f3 Realtek Semiconductor Corp. <---webcam
Bus 001 Device 003: ID 8087:0a2a Intel Corp. <-- bluetooth
Bus 001 Device 007: ID 054c:0268 Sony Corp. Batoh Device / PlayStation 3 Controller <-- PS3 joystick
Bus 001 Device 005: ID 0079:0006 DragonRise Inc. PC TWIN SHOCK Gamepad <-- arkade joystick
Bus 001 Device 006: ID 1eaf:0024 <-- STM32

arpruss Wed Dec 20, 2017 2:17 am

Sure. When you call usb.begin(), you can do something like usb.begin(HID_JOYSTICK, 0, 0, “I made it”, “My funny joystick”). See USBHID.h for all parameters. If you want, you can replace the 0,0 with your favorite VID and PID numbers, e.g., to fool the OS into thinking you’ve got a different device than you do.

arpruss Thu Dec 21, 2017 4:47 pm

I just switched the default PID from 0x24 to 0x04, to match the standard core PID.

arpruss Wed Jan 03, 2018 6:06 pm

I added a no-wait option for output/feature host-to-device, so that the USB HID handler silently updates the output/feature buffers in the background without polling being needed to ensure that the sketch doesn’t miss data.

And added a keyboardwithleds example that echoes keyboard LED status to serial.

Sign in to reply