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

nicolas_soundforce Sat Mar 03, 2018 12:00 pm

This is fantastic, I was using the addMidiBranch until now, I can’t wait to try it out. Thank you so much for the effort!

Is there a way to set the device name in the user program ? So far I was editing the header files. It worked but I am switching often so not very practical.

arpruss Sat Mar 03, 2018 3:12 pm

[nicolas_soundforce – Sat Mar 03, 2018 12:00 pm] – Is there a way to set the device name in the user program ? So far I was editing the header files. It worked but I am switching often so not very practical.

USBComposite.setManufacturerString("My Garage");
USBComposite.setProductString("My Thingy");

arpruss Sat Mar 03, 2018 3:17 pm

I noticed that if you had capslock on in my Windows keyboard, the capslock state affected the USB HID keyboard as well. That would probably result in undesired operation. For instance, if your stm32f1 device was a password manager, a capslock state on the host keyboard would mangle things. My quick and dirty fix was to use the LED capslock status in the keyboard code to swap case as needed. Now, Keyboard.println(“Hello world”) will type “Hello world” regardless of the capslock state of a Windows host. It would be nice if someone with a conveniently accessible Linux system were to check if this works for them, too.

If you like this behavior, you must call Keyboard.begin() before calling any keyboard functions. (Normally, Keyboard.begin() is optional, but it’s needed for LED state.)

If you don’t like this behavior, you can call Keyboard.setAdjustForHostCapsLock(false).

nicolas_soundforce Sat Mar 03, 2018 9:32 pm

[arpruss – Sat Mar 03, 2018 3:12 pm] –
[nicolas_soundforce – Sat Mar 03, 2018 12:00 pm] – Is there a way to set the device name in the user program ? So far I was editing the header files. It worked but I am switching often so not very practical.

USBComposite.setManufacturerString("My Garage");
USBComposite.setProductString("My Thingy");

arpruss Sun Mar 04, 2018 5:18 am

[vitor_boss – Sat Mar 03, 2018 5:51 pm] –
Hi, this code could run as HID only?

Sure. Do you mean raw hid? There is example code for that.

Cpt.Credible Tue Mar 06, 2018 12:14 pm

I am having a hard time with the issue where Windows no longer detects the USB device.

As mentioned earlier, after mucking about trying to set up MIDI and serial in parallel for debugging, occasionally setting code up that results in “device malfunctioned” And suddenly, for reasons unbeknownst to me, my computer will no longer detect the MIDI USB device. It still detects the Maple Serial device though. And If I plug it into a different machine, it shows up fine as both the maple serial device and the Midi device.

And when I say it doesn’t show up, I mean it doesn’t show up at all. Windows doesn’t acknowledge that it exists at all. no “unknown device” no “device malfunctioned” it is completely invisible to both device manager and the tool USBdeview by Nirsoft.

[arpruss – Thu Mar 01, 2018 2:19 pm] –
You can try deleting the old driver in the Windows device manager, and then hotplugging again, or changing the product id in the code. E.g., in my midiin example, add midi.setProductId(0x25) before calling midi.begin().

As arpruss suggested, changing the product ID to something else using USBComposite.setProductId() allows my computer to detect and use the MIDI device, but that breaks the maple serial driver that is expecting a Product ID of 4. And on the note of deleting drivers, no amount of driver deletion seems to help. I even used USBdeview to uninstall ALL USB devices ever connected to the machine to no avail.

This magical disappearing has happened on 2 of my machines now, one win10 and one win7 and I appreciate that this is not really an issue with this glorious library, more an issue with my ignorance and my PC. But I am tearing out my hair and was wondering if anybody knows how to fix this.

Is the device blacklisted somewhere at a low level? Is it some hardware on the motherboard that has decided to start ignoring this device? should I just give in and use hardware serial for debugging (not optimal as it seems to be blocking and causing audio dropouts (I’m making a synthesizer))

Any insight or help would be greatly appreciated

EDIT:
All tools for removing “ghost devices” and removing all traces of previously connected devices failed, “Ghostbuster” and “USBOblivion”. What worked though was programming the stm32f103c with the midiout example, upon which it erroneously showed up as a Serial device that wouldn’t start. Right clicking and selecting remove device and uninstall driver allowed it to be rediscovered as a midi device.

florisla Tue Mar 06, 2018 9:04 pm

[Cpt.Credible – Tue Mar 06, 2018 12:14 pm] –
And when I say it doesn’t show up, I mean it doesn’t show up at all. Windows doesn’t acknowledge that it exists at all. no “unknown device” no “device malfunctioned” it is completely invisible to both device manager and the tool USBdeview by Nirsoft.

When it’s gone missing, do you do Action -> Scan for hardware changes? I’ve had to do that on many occasions so Windows would ‘re-discover’ a device for which I removed (disabled + deleted!) the driver.

Also, does a reboot of the PC help at all?

Cpt.Credible Wed Mar 07, 2018 8:19 am

[arpruss – Wed Mar 07, 2018 5:49 am] –
1. You can install the serial driver for a different product id. Just edit the batch file.

That is handy to know!

[arpruss – Wed Mar 07, 2018 5:49 am] –
2. Have you set your device manager to view hidden devices? https://technet.microsoft.com/en-us/lib … 84583.aspx

Yes, and rebooted, but as I mentioned in the edit it seems like somewhere there was something data I couldn’t find in the registry or using any of the USB device fixing tools I have found that made the computer confused about the MIDI device. Making it show up as ONLY the midi device using the midiout example and then uninstalling the incorrectly recognized “Serial device unable to start” in device manager, fixed the situation. I spoke to some other developers familiar with developing USB and the message I got is that this is a known pain in windows and I should be using linux to develop something like that :)

On a related note, is there anything else needed in setup() in the midiin example in order to use serial alongside with midi? should there be a CompositeSerial.begin(250000) or something? or is that all handled in the USBComposite.begin()? should I be able to just add a CompositeSerial.println(“Hello world”) in the main loop?

arpruss Wed Mar 07, 2018 2:55 pm

[Cpt.Credible – Wed Mar 07, 2018 8:19 am] –
On a related note, is there anything else needed in setup() in the midiin example in order to use serial alongside with midi? should there be a CompositeSerial.begin(250000) or something?

No. See the README file, which alas is still unfinished. CompositeSerial is a plugin, and the only time you call a plugin’s begin() method is if you are making a simple USB device (with only one plugin). Otherwise, you register the plugins with plugin.registerComponent() and call USBComposite.begin().

or is that all handled in the USBComposite.begin()? should I be able to just add a CompositeSerial.println(“Hello world”) in the main loop?

All you need is the CompositeSerial.println(). Note that there already are calls to CompositeSerial.println() in the noteOn and noteOff handlers in the example.

arpruss Wed Mar 07, 2018 11:22 pm

[Freakeyyy – Wed Mar 07, 2018 10:19 pm] – I was wondering what was the difference between “BootKeyboard” and “Keyboard ?

BootKeyboard cannot be composited with another USB device, but works in system BIOS.

Also is it possible to have NKRO capability for the keyboard ? It would be interesting for building mechanical keyboards.

The default is 6KRO. You can try to increase that to any number you wish, at the expense of slowing down the data transmission and using a bit more memory. To increase it, edit USBHID.h and change the HID_KEYBOARD_ROLLOVER value. This is untested: let me know if it works for you.

It’s possible you will run into trouble above 62. Or above 6. Like I said, it’s not tested.

Cpt.Credible Thu Mar 08, 2018 4:38 pm

[arpruss – Wed Mar 07, 2018 2:55 pm] –
Note that there already are calls to CompositeSerial.println() in the noteOn and noteOff handlers in the example.

Are we using the same example? I am using https://github.com/arpruss/USBHID_stm32 … midiin.ino
all I can see in the in the noteOn and noteOff handlers are calls to tone() and noTone()

Is there a more recent version of the library I should be using?

arpruss Fri Mar 09, 2018 3:54 am

[Cpt.Credible – Thu Mar 08, 2018 4:38 pm] –
[arpruss – Wed Mar 07, 2018 2:55 pm] –
Note that there already are calls to CompositeSerial.println() in the noteOn and noteOff handlers in the example.

Are we using the same example? I am using https://github.com/arpruss/USBHID_stm32 … midiin.ino
all I can see in the in the noteOn and noteOff handlers are calls to tone() and noTone()

Is there a more recent version of the library I should be using?

Oh, I guess I had the code inserted for testing purposes, but not committed.

arpruss Mon Mar 12, 2018 1:30 am

I added the convenience USBComposite.isReady() method for checking if the USB device is connected with the host and configured. Checking this before sending data will prevent some hangs if the USB connection gets disconnected. (Some but not all hangs. There is always a chance of a disconnect between the isReady() call and the transmission.)

madias Wed Apr 04, 2018 10:56 am

I played a little bit around with this superb approach getting USB HID to work
Board: Maple mini
  • SD-Card example works better as expected (decent speed, even without “ex” – I was even able to play MP3 from card)
    “Consumer” example is tricky on my Macbook ;) (Hitting the two small buttons right (perpetual bootloader) before the light goes out!)
    The mass storage example does not work: Even on Windows or OSX the generated containing device is empty (no files on it). Has anybody checked this?

On OSX (El Captain 10.11.6, Macbook Pro middle 2009) playing with USB-HID can be dangerous. After some examples (I think “consumer”) I wasn’t able to communicate with the maple mini anymore (not even in perpetual bootloader mode or rebooting the system). So I flashed under Windows a simple empty code for getting the maple working again. But this is a OSX specific problem, so it should not matter)

But all in all: Everything is working correct and it’s a major step forward using USB-HID stuff without using an alternative Repro.

So: Thanks Arpuss!

arpruss Wed Apr 04, 2018 2:56 pm

[madias – Wed Apr 04, 2018 10:56 am] –
The mass storage example does not work: Even on Windows or OSX the generated containing device is empty (no files on it). Has anybody checked this?[/list]

I am pretty sure the image in image.h is empty. But you may be able to copy a small file to the drive from the host. At least, last I was testing it on Win10, I could. It wouldn’t surprise me, however, if some operating systems had trouble with a FAT-12 image that is less than 12K in size.

Of course, any file you copy will disappear when you power off the device, as the storage is in RAM.

On OSX (El Captain 10.11.6, Macbook Pro middle 2009) playing with USB-HID can be dangerous. After some examples (I think “consumer”) I wasn’t able to communicate with the maple mini anymore (not even in perpetual bootloader mode or rebooting the system). So I flashed under Windows a simple empty code for getting the maple working again. But this is a OSX specific problem, so it should not matter)

Maybe setting a different USB product ID from the default one when experimenting would help? I am guessing that OSes get confused when you switch USB device types without changing the vendor/product IDs.

john.carter3 Wed Apr 11, 2018 5:44 pm

[michael_l – Fri Feb 02, 2018 4:22 pm] –
I hacked earlier SPIFFS to work with WDxxx SPI flash memories on stm32duino. It works but the code is a bit *couch* sketchy. It’s really not a fast fs but feasible for small projects. Together with MSC that would make a nice addition.

I wonder if current libmaple/stm32duino have ready made functions for reading/writing from flash memory ?

Do you share the SPIFFS hacked?

arpruss Sat Apr 14, 2018 3:05 pm

A quick note: The library is now bundled with Roger’s core.

Sign in to reply