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

madias Sat Apr 14, 2018 7:54 pm

+1
For me the USB-HID (library) is one of the best features even a little bluepill can provide making it to an outstanding little beast.

Cpt.Credible Fri Apr 27, 2018 2:45 pm

[arpruss – Sat Apr 14, 2018 3:05 pm] –
A quick note: The library is now bundled with Roger’s core.

sweet

nicolas_soundforce Sat May 12, 2018 7:16 am

When I am using the MIDI inputs handlers, the blue pill seems to crash, even when doing nothing in those.
So as soon as the Blue Pill USB MIDI device receives a MIDI message, the main program simply stops running.

Anybody sucesfully used USB MIDI input ? Is this tested ?

I am using the version including in the last repo.

Thanks,
Nicolas.

class myMidi : public USBMidi {
virtual void handleNoteOn(unsigned int channel, unsigned int note, unsigned int velocity) {

}

virtual void handleNoteOff(unsigned int channel, unsigned int note, unsigned int velocity) {

}

virtual void handleProgramChange(unsigned int channel, unsigned int program) {

}
};

arpruss Sun May 13, 2018 1:37 pm

Does the midi input example code work for you?

Mirko Thu Jun 07, 2018 11:32 am

Let me start with a big “Thank you” @ arpuss. Your USB HID library is working quite well for me …. had to uninstall the Maple serial driver to get it work but now it is working very well :D THANKS!

I am building a sim racing button box with LCD display, magnetic shifters (joystick buttons) and dual clutch paddles (joystick axis).
While testing the joystick axis I noticed that I can only send values from 0 to 1023 for the joystick movement.
When reading my analog clutch (analogRead) I have a 12bit resolution (0 to 4096) which I would like to retain.

In “Joystick.cpp” you have
void HIDJoystick::X(uint16_t val){
if (val > 1023) val = 1023;
joyReport.x = val;

safeSendReport();
}

Pito Fri Jun 08, 2018 7:44 am

Started to play with the HID library (so be patient with me, a standard user only).
Tried sdcardreader, mass, absmouse, jigglemouse and mouse jiggler examples.
With Blue103ZET (sdcardreader, STLink upload) and the BluePill (the others, stm32duino bootloader latest).
I uninstalled the HID-bootloader device from my previous exercises.

I got “USB device not recognised” or it installed Maple Serial with SerNum 00000000000000000001 and I see no activity or any special HID device installed (watched with usbdeview).
Win7 64bit here.
Can somebody confirm some of the examples work with Win7 64bit and BPill plz?
What I missed or did wrong way?

PS: I managed to get “mass” working, it even remembers the disk content when plugged off the usb port (powered via stlink) :)
No fun with mouse yet..

Pito Fri Jun 08, 2018 4:25 pm

With flashed in a mouse demo, when I insert BPill into my notebook, the mouse arrow always jumps into the middle when moved somewhere, so it seems it works somehow.
I get this usbdeview:
Maple Composite.jpg
Maple Composite.jpg (59.32 KiB) Viewed 760 times

ag123 Fri Jun 08, 2018 5:08 pm

wanted to get started playing with this as well, oh btw my guess is if you use a vid/pid that of a mouse / keyboard and show up as a hid, windows may ‘auto install’ the necessary drivers :)

Pito Fri Jun 08, 2018 6:46 pm

So, tried again with my 103ZET (64kB+512kB of sram and an sdcard socket).
Flashed in the stm32duino bootloader (the generic one). Maple Serial uninstalled (driver deleted).
Compiled (Sloeber) the sdreader demo (set the sdcard’s /CS and the LED pins properly, SdFat (not Ex)), uploaded.

Inserted a 16GB CL10 samsung sdcard into the 103ZET’es socket.

After re-plugging the 103ZET (and cleaning up the old mess with usbdeview), waiting several seconds, I got a window saying the sdcard needs to be reformatted (used in android). I put the sdcard off and reformatted under Win7 (SDformatter). Put the sdcard back into 103ZET, re-plugged and I got again the window saying sdcards needs to be reformatted.

I clicked ok and got the standard Win formatting window offering me beefy 2.92GB/FAT32 size. Ok, understood, started with formatting. After about a ~minute it finished fine :shock: and I re-plugged the 103ZET again.

After about 10 secs waiting I got a window with a new drive E:, opened it, was empty, therefore I started to copy a 160MB large Sloeber zip file on it. The copying started with a standard copy window, indicating 13minutes and 216kB/sec speed. While storing the file I wrote this report :)
It finished ok.

Re-plugged the 103ZET again. Opened E: and checked the file’s sha-1 (took several minutes to read) and it passed.. Nice.. 8-)

PS: I put the sdcard off the 103ZET’es sdcard socket and double checked under Win7 again, the sha-1 passed..

arpruss Fri Jun 08, 2018 7:04 pm

I am not sure why the reformatting was needed. If you can figure it out, that would be great.

Note that my mouse example is an absolute rather than the more usual relative mouse. (The JiggerMouse example that I added yesterday is a relative mouse.)

ag123 Sat Jun 09, 2018 2:36 am

[Pito – Fri Jun 08, 2018 6:46 pm] –
So, tried again with my 103ZET (64kB+512kB of sram and an sdcard socket).
Flashed in the stm32duino bootloader (the generic one). Maple Serial uninstalled (driver deleted).
Compiled (Sloeber) the sdreader demo (set the sdcard’s /CS and the LED pins properly, SdFat (not Ex)), uploaded.

Inserted a 16GB CL10 samsung sdcard into the 103ZET’es socket.

After re-plugging the 103ZET (and cleaning up the old mess with usbdeview), waiting several seconds, I got a window saying the sdcard needs to be reformatted (used in android). I put the sdcard off and reformatted under Win7 (SDformatter). Put the sdcard back into 103ZET, re-plugged and I got again the window saying sdcards needs to be reformatted.

I clicked ok and got the standard Win formatting window offering me beefy 2.92GB/FAT32 size. Ok, understood, started with formatting. After about a ~minute it finished fine :shock: and I re-plugged the 103ZET again.

After about 10 secs waiting I got a window with a new drive E:, opened it, was empty, therefore I started to copy a 160MB large Sloeber zip file on it. The copying started with a standard copy window, indicating 13minutes and 216kB/sec speed. While storing the file I wrote this report :)
It finished ok.

Re-plugged the 103ZET again. Opened E: and checked the file’s sha-1 (took several minutes to read) and it passed.. Nice.. 8-)

PS: I put the sdcard off the 103ZET’es sdcard socket and double checked under Win7 again, the sha-1 passed..

wow, that’s pretty good, i’m wondering how far we can push the 216 KB/s speed, in theory usb 2.0 full speed is capable of 10 mbps which i’d guess could give closer to 1MB per sec :D

Pito Sat Jun 09, 2018 7:14 am

It seems the speed is somehow limited with USB and overhead (stm32F103ZET @72MHz).
With SdFat or SdFatEX, and
if (sd.begin(SD_CHIP_SELECT, SD_SCK_MHZ(36)))

ag123 Sat Jun 09, 2018 4:30 pm

i think the ‘reformatting’ issue is probably due to a format that windows don’t recognise or deem corrupt
i’ve had some sd cards with linux partitions which windows give those ‘reformat’ prompts with ‘ordinary’ sd card readers
however, i’m not too sure if there is anything else more than the on disk/card partition (data) format which may cause the prompts

fredbox Sat Jun 09, 2018 5:16 pm

Back in the days when the original Barnes and Noble Nook was a hot e-reader, there was great interest in booting from a micro sd card. At that time, some observed that class 10 sd cards performed noticeably worse than some class 2 cards. The trick to getting shorter boot times was to use a benchmark utility like CrystalDiskMark and find a card that had good 4K read/write performance. Most all cards had good performance on large files, but only a few had good 4K performance. There was a Sandisk class 2 card that outran them all. This is from nearly 10 years ago, I suspect the sd cards have improved in the meantime.

ag123 Sat Jun 09, 2018 5:46 pm

i’d think at this stage usb mass storage is probably not (yet) tuned/optimised to the max, tuning it is labor intensive involving finding out where are the ‘spaces’ that could be utilized better (e.g. ‘standalone’ mass storage rather than combine with hid / serial etc) . my guess is that if one is able to run it on better hardware e.g. stm32f103 with more sram – the high density series or even for that matter a stm32f407 and pushing the limits of spi so that the speeds between sd cards and stm32 is at least 1 MB per sec, we’d probably see how far usb 2.0 full speed can achieve for usb mass storage.

tuning / optimizing it on the stm32f103 c8 or cb could be rather challenging given its 20k sram limits, if we keep a ‘buffer’ of 4k there is 16k left for ‘everything else’ and usb needs its own buffers. currently my guess is that most of the classes and libraries are pretty much modular than ‘optimised to bare metal’, in a sense if we do dedicated optimization between spi and usb and solely for usb mass storage it could pretty much push the limits of what is possible. but keeping it modular would simplify maintenance, make codes more readable, less bugs etc and that it would be easier to ‘plug and play’ modules

i’m thinking that a usb bus with several devices could also be very busy and could in itself be its own bottleneck rather than stm32 itself
just 2 cents

Pito Sun Jun 10, 2018 8:40 am

With an old 512MB sdcard:
1. 10MB file copy PC->BlueZET: 33secs, 303kB/sec
2. 10MB file copy BlueZET->PC: 21secs, 476kB/sec
With the 10MB file on the sdcard the E: window appears 3secs after plugging in the BlueZET into the usp port.

ag123 Sun Jun 10, 2018 1:23 pm

that looks pretty fast even in an ‘unoptimised state’, but what seem a little less certain is that vs usb throughput of 10 mbps full speed max, i’m not too sure if usb host multiplex between devices and wait a little in turn for each device etc.

f3flight Mon Jun 11, 2018 12:53 am

Hi arpruss,

I’m running into an issue when using your lib, basically the device hangs as soon as it goes into any code related to USB. First of all, no matter which example I test, I only see “Maple Serial” in devices (using Windows),so no additional devices appear. If I just use blink example it works ok, so there’s no issue with flashing the chip. However if I use any of the USBComposite examples, the board doesn’t do anything. If I modify example to blick a few times before executing USB function (i.e. before Keyboard.print()), it blinks and then hangs. Commenting use of such USB function makes the device run it’s loop without issues.

Any ideas why this could be? I think the most direct indicator of the issue is that no additional device appears in the system. Could it be because I have not replaced the default 10kOhm resistor R10? As I mentioned before, device shows up as DFU ok (when using bootloader_only bootloader), as well as Serial (when using bootlear+empty scketch type of bootleader), and I can reflash it using Arduino IDE “stm32duino bootloader” mode without issues. I uploaded bootleader using J-Link, if that matters.

f3flight Mon Jun 11, 2018 4:32 pm

Pito,

Thank you, that helped!

arpruss, Pito,

Now I’ve found out that USBMIDI is not compatible with other components. WHich is not the case with main Arduino libs, there they’re based on some “pluggableUSB” module and I can instantiate Serial + Keyboard + Mouse + MIDI just by including headers Keyboard.h, Mouse.h MIDIUSB.h and having “Keyboard.begin()…” in my setup. With USBComposite I have this code:

#ifdef _LIBMAPLE_STM32_H_ //my goal is to make the same app compile on stm and arduino so I'm testing this #ifdef thing... works ok
#include <USBComposite.h>
#include <USBMIDI.h>
#endif

void setup() {
USBHID.setReportDescriptor(hidReportKeyboardMouse); // most likely this is not compatible as MIDIUSB is also a HID device?
USBComposite.clear();
CompositeSerial.registerComponent();
USBHID.registerComponent();
//USBMIDI.registerComponent(); // if I uncomment this, the device is not recognized
USBComposite.begin();
}

void loop() {
delay(10000);
}

Sign in to reply