USB HID / MIDI / Mass Storage as libraries

Please do not post requests
Post Reply
arpruss
Posts: 156
Joined: Sat Sep 30, 2017 3:34 am

USB HID / MIDI / Mass Storage as libraries

Post by 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: 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:

Code: Select all

#include "GameControllers.h" // https://github.com/arpruss/GameControllersSTM32
#include "USBHID.h"

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);
}
**Changed for latest API**
Last edited by arpruss on Mon Feb 05, 2018 4:10 am, edited 12 times in total.

User avatar
RogerClark
Posts: 7693
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: USB HID / USB MIDI as libraries

Post by RogerClark » Sun Dec 03, 2017 9:16 pm

Any idea how we could stop the code hanging if calls are made to Serial ?

arpruss
Posts: 156
Joined: Sat Sep 30, 2017 3:34 am

Re: USB HID / USB MIDI as libraries

Post by arpruss » Sun Dec 03, 2017 10:51 pm

RogerClark wrote:
Sun Dec 03, 2017 9:16 pm
Any idea how we could stop the code hanging if calls are made to Serial ?
Actually, my note about hanging was unduly alarmist: when I actually tried it, there was no hang.

Nonetheless, to be safe, I just added a USBSerialNOP class that extends USBSerial and does nothing, and made the init code for HID and MIDI overwrite Serial with an instance of USBSerialNOP. We still waste flash space by having the Serial code, but at least we won't have any issues with interference.

User avatar
RogerClark
Posts: 7693
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: USB HID / USB MIDI as libraries

Post by RogerClark » Sun Dec 03, 2017 11:59 pm

OK

Thanks

I'll give the libs a try when I get chance (possibly this evening)

arpruss
Posts: 156
Joined: Sat Sep 30, 2017 3:34 am

Re: USB HID / USB MIDI as libraries

Post by arpruss » Mon Dec 04, 2017 3:57 pm

By the way, does the Serial.begin() call have to be there in variants/*/wirish/boards_setup.cpp : board_setup_usb()? All the sketches I've looked at or written that use serial call Serial.begin() in setup() anyway.

If the Serial.begin() call were removed from the global setup code in the core, and if one left the issues about using Serial by mistake for the user to worry about out, then the linker wouldn't link in the USBSerial and usbcdcacm code, and library users would save flash. It's that one call to Serial.begin() in the default board_setup_usb() that gets the linker to pull in the usb serial code.

What's weird is that board_setup_usb() is declared __weak and I override it in the library. The override works, but the stupid(?) linker still pulls in the dependencies for the original __weak board_setup_usb(). This happens even in LTO mode, which make no sense to me. But if one comments out the Serial.begin() line in the core, all that unnecessary code disappears.

arpruss
Posts: 156
Joined: Sat Sep 30, 2017 3:34 am

Re: USB HID / USB MIDI as libraries

Post by arpruss » Mon Dec 04, 2017 5:21 pm

Nevermind the suggestion to remove Serial.begin(). It's needed for the serial-reset functionality. :-(

It would still be nice to find some way to override it without bringing in the unnecessary dependencies.

User avatar
RogerClark
Posts: 7693
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: USB HID / USB MIDI as libraries

Post by RogerClark » Mon Dec 04, 2017 7:56 pm

Yes...
Unfortunately, the Serial.begin is essential so that the core can be triggered to reboot via the IDE

I did look to see if commands can be sent via HID which could be uses to reboot, but i could not find much useful information about how this could be done using the generic Windows HID driver

arpruss
Posts: 156
Joined: Sat Sep 30, 2017 3:34 am

Re: USB HID / USB MIDI as libraries

Post by arpruss » Tue Dec 05, 2017 1:18 pm

Maybe the best solution would be to add a menu option that turns off USB Serial in the core. This would be like the addMidiHID branch, but easier to maintain, since all the actual Midi and HID support would be pushed out into libraries, and with two usb menu options instead of four or five. And once ready someone puts in the effort, they can write a HID+serial composite library.

User avatar
RogerClark
Posts: 7693
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: USB HID / USB MIDI as libraries

Post by RogerClark » Tue Dec 05, 2017 7:59 pm

It would be good if we could add the menu, but its not that simple, because several of the upload methods already dont have USB Serial turned on.

So it would break things for anyone currently using Serial or BMP upload, if it was defaulted to being turned on.

Unfortunately, the menu system in the IDE is not cascading, so there is no way to just have this as an option for the upload methods which currently have USB Serial turned included in the build.


From what I understand, having USB Serial initially turned on, means the code is bigger, but your code turns it off.

i dont see this as a big problem, because conversely, someone may want to use USB HID to start with, but then switch back to USB Serial later in their code. In which case USB Serial would need to be compiled in.

Most BluePills have 128k so I dont think an additional 5k all the time is a big problem


if the problem is that USB Serial starts and then stops again ( because the HID library disables Serial), perhaps there is a way to add a global flag which is set as part if the HID library, which prevents the code calling Serial.begin() in the core.

arpruss
Posts: 156
Joined: Sat Sep 30, 2017 3:34 am

Re: USB HID / USB MIDI as libraries

Post by arpruss » Thu Dec 07, 2017 3:51 am

I've replaced the three Keyboard-Mouse / Joystick / Keyboard-Mouse-Joystick libraries with a single USB HID library. This requires a single initialization line to select between options:

Code: Select all

USBHID.begin(device);
where device is one of:

Code: Select all

    HID_MOUSE,
    HID_KEYBOARD,
    HID_JOYSTICK,
    HID_KEYBOARD_MOUSE,
    HID_KEYBOARD_JOYSTICK,
    HID_KEYBOARD_MOUSE_JOYSTICK,
You can also have a partially or completely custom HID report. For instance, here is a monster composite USB device with two joysticks, a keyboard and a mouse.

Code: Select all

#include <USBHID.h>

const uint8_t reportDescription[] = {
   HID_MOUSE_REPORT_DESCRIPTOR(),
   HID_KEYBOARD_REPORT_DESCRIPTOR(),
   HID_JOYSTICK_REPORT_DESCRIPTOR(),
   HID_JOYSTICK_REPORT_DESCRIPTOR(HID_JOYSTICK_REPORT_ID+1),
};

HIDJoystick Joystick2(HID_JOYSTICK_REPORT_ID+1);

void setup(){
  USBHID.begin(reportDescription, sizeof(reportDescription));
}

void loop(){
  Joystick.X(0);
  Joystick.Y(0);
  Joystick.sliderRight(1023);
  delay(400);
  Joystick.X(1023);
  Joystick.Y(1023);
  Joystick.sliderRight(0);
  delay(400);
  Joystick2.X(0);
  Joystick2.Y(0);
  Joystick2.sliderRight(1023);
  delay(400);
  Joystick2.X(1023);
  Joystick2.Y(1023);
  Joystick2.sliderRight(0);
  delay(400);
}
Finally, you can use USBHID.begin() to change the manufacturer/product IDs and strings (ASCII only).

**changed for latest API**
Last edited by arpruss on Sun Jan 07, 2018 4:16 pm, edited 7 times in total.

Post Reply