MIDI SendSysEx on USB host

Hi. I'm trying to achieve something similar to the THR_Footswitch by Mathis Rosenhauer (LINK) but with a Teensy 3.6 and its second USB port. The THR10 guitar amplifier is a MIDI Device, so the Teensy must act as an USB host. The THR10 will be the only device that is connected to the Teensy. All that is missing is the implementation of my SendToMidi() method, which shall be able to send an array of bytes of any size like this:

bool SendToMidi(byte[] byteArray, int nLength)
{
// Use SendSysEx to send byteArray to the THR10 over USB
}

Currently, I only want to send SysEx-Data and not receive anything. I am aware of USBHost_t36, but I wasn't able to find an example for this (presumably) simple task. Can anybody show me how to

- Setup() USB MIDI for the second USB port
- Implement the beforementioned SendToMidi() function

Any help is highly appreciated.

Karl
 
In Arduino, click File > Examples > USBHost_t36 > MIDI > Interface_16x16.

Admittedly this is a long and somewhat complex example, because it allows 16 MIDI ports on your PC to communicate with 6 serial (5 pin DIN) MIDI devices and up to 10 more USB MIDI devices plugged into hubs on the host port. Look for this line, which sends to one of the 10 devices on the host port.

Code:
        default: // cases 6-15
          midilist[cable - 6]->sendSysEx(SysExLength, usbMIDI.getSysExArray(), true);

USB host uses the same MIDI functions as the MIDI library and USB device MIDI. If you've only created 1 "MIDIDevice" instance (rather than 10 plus several hubs as the example does), because you only wish to support 1 device plugging into the host port, then you won't need to bother with an array of pointers. You can just use the name directly, like...

Code:
  midi01.sendSysEx(length, messageData, true);

The "length" is how many bytes are in your message, messageData is the array holding those bytes, and the last true/false tells whether your data already has the sysex begin & end bytes (if you give false, the library adds them for you).

It's always safe to send message to your MIDIDevice object. If the user hasn't actually plugged in a MIDI device to the host port, it will simply do nothing and discard the data (pretty much the same as sending to a regular serial MIDI port where nothing is plugged in).
 
Thank you very much, Paul. This clears things up quite a bit.

In his code, Mathis Rosenhauer sends a prefix, then the actual patch data and lastly a suffix with a checksum and an end-byte like this:

Code:
void send_patch(uint8_t patch_id)
{
    uint8_t prefix[] = 
    {
        0xf0, 0x43, 0x7d, 0x00, 0x02, 0x0c, 0x44, 0x54,
        0x41, 0x31, 0x41, 0x6c, 0x6c, 0x50, 0x00, 0x00,
        0x7f, 0x7f
    };

    uint8_t cs = 0x71; // prefix checksum

    Midi.SendSysEx(prefix, sizeof(prefix));

    // Mathis uses a loop to split the patch data into chunks of 16 bytes, sends each chunk and updates the checksum cs
    // for each chunk individually, but basically, he just sends out the entire 261 bytes of the patch
    Midi.SendSysEx(chunk, sizeof(chunk));

    // Calculate check sum.
    suffix[0] = (~cs + 1) & 0x7f;

    // end SysEx
    suffix[1] = 0xf7;
    Midi.SendSysEx(suffix, sizeof(suffix));
}

Can I assume that, if I use midi01.sendSysEx(length, messageData, true), sendSysEx() will create this exact same prefix and suffix for me?

Cheers,
Karl
 
The 3rd true/false parameter merely tells if you have already put 0xF0 in the first byte and 0xF7 in the last byte in your data array. It's really that simple.

This code fragment you've show sends 3 sysex messages. The true/false applied separately to each one. The first definitely has 0xF0 and 0xF7, so you would use "true". The other 2 presumably have these too, though this code depends on other variables not declared in the small fragment you showed, so that's just my assumption.

The true/false parameter is provided as a convenience, since some people prefer to write their code to create only the data part of the sysex message, omitting the leading 0xF0 and 0xF7.

This parameter is something completely separate from the use of 3 messages, prefix, data, checksum. As far as USBHost_t36 is concerned, this is just 3 individual sysex messages. The library doesn't know or care that these 3 messages are related. The 3rd true/false does NOT have anything to do with use of multiple messages. It applies only to each individual message.

For example, if you wanted, you could transmit the prefix with that code, where 0xF0 & 0xF7 are already in the array using "true". Then if you happen to have only the data in an array, you could transmit it with "false" if you wanted to avoid the extra step of putting 0xF0 in the first byte of your array and 0x7F in the last byte. Likewise, you could send the checksum message either way, because the true/false parameter is for each individual message.

As far as the library is concerned, it's just 3 separate & independent sysex messages. The fact that these 3 message go together and mean something as a group of 3 is up to your program. The library just handle them as 3 completely separate messages.
 
Thanks a lot, Paul. I think, I got it now. The prefix and suffix Mathis sent out in his code are not part of the MIDI protocol, as I assumed, but specific to the THR10 (except for the leading 0xF0 and the trailing 0xF7). I'll have to send out exactly the same data as Mathis, but I will probably send out all data at once from one large array and not in multiple steps. I'll report when I'm done.
 
I managed to test my USB Host Connection with the InputFunctions example. I receive MIDI events when I use a MIDI keyboard. But I don't receive anything when I connect the THR10 instead.
When I run a MIDI monitor on my Windows PC, I see tons of events from the THR10, so I know that it is continuously sendig out MIDI SysEx commands. Any ideas why the Teensy doesn't see them?
 
The THR10 is a device. It has the same female USB-B connector as most MIDI controllers, like this one. Mathis Rosenhauer was using the Arduino USB Host Shield in his project. The THR10 is usually connected to a computer in order to use the software editor that Yamaha provides.

Some smart people found out that the THR10 is using standard MIDI SysEx (and only SysEx) for all communication between the software editor and the device. Every aspect can be controlled via MIDI.
https://www.thegearpage.net/board/index.php?threads/yamaha-thr-android-editor.1669012
https://drive.google.com/file/d/0ByMjgI3-DPShZkRLM0VPSkhkelk/view

Can you confirm that InputFunctions example should also show some output for SysEx messages and not only for known MIDI commands like note-on etc.?
 
Can you confirm that InputFunctions example should also show some output for SysEx messages and not only for known MIDI commands like note-on etc.?

Yes, it should.

In fact, there are 2 different sysex handlers.

Code:
  // Only one of these System Exclusive handlers will actually be
  // used.  See the comments below for the difference between them.
  midi1.setHandleSystemExclusive(mySystemExclusiveChunk);
  midi1.setHandleSystemExclusive(mySystemExclusive);

Maybe try editing the code so only 1 of these lines is used. Hopefully at least 1 of them will work.
 
I made a test with an original Arduino Uno, an original USB Host Shield and the USB_Host_Shield_Library_2.0. With this setup the communication with the THR10 works flawlessly. So what we have so far is:

  • I can monitor MIDI-traffic between the THR10 and my Windows PC
  • Communication works with an Arduino and the Arduino USB Host Shield, no matter, what MIDI device I connect
  • Communication works with the Teensy 3.6, the onboard USB Host connector and a MIDI keyboard. The keyboard is powered via the USB connection
  • Communication does not work with the Teensy 3.6, the onboard USB Host connector and the THR10 (same cable as before). The THR10 is self-powered.

My guess is that the THR10 does not recognize the Teensy for some reason. What could be possbile reasons for this? USB power? Software/Library? Any other ideas?

Regards,
Karl
 
Maybe THR10 needs larger buffers (for 480 Mbit speed)?

Can you try editing USBHost_t36.h. Look for these 2 lines inside the MIDIDevice class. Currently they're at line 1178.

Code:
        enum { MAX_PACKET_SIZE = 64 };
        enum { RX_QUEUE_SIZE = 80 }; // must be more than MAX_PACKET_SIZE/4

Try changing them to this:

Code:
        enum { MAX_PACKET_SIZE = 512 };
        enum { RX_QUEUE_SIZE = 2048 }; // must be more than MAX_PACKET_SIZE/4

Before you click Verify or Upload, make a note of the "Global variables use XXXXX bytes" message Arduino prints. With this change, you should see the memory usage increase, since you're using much more RAM for buffers. This is the main way you can check if Arduino really used your edit to the file.

Do larger buffers help with the THR10? Last week I personally tested with an OP-1, where this fixed all the issues (at least with the OP-1 in MIDI mode, we didn't try reconfiguring it to other modes)

I'm considering increasing the buffer size, or maybe offering 2 different variants of MIDIDevice. Feedback with other devices can really help.
 
Alternately, can you try uncommenting this line in ehci.cpp?

Code:
        //USBHS_PORTSC1 |= USBHS_PORTSC_PFSC; // force 12 Mbit/sec

As you can guess from the comment, this will prevent use of 480 Mbit/sec speed.

UHS20 and the USB Host Shield aren't using 480 speed. Many devices give a different set of descriptors and may actually work differently, depending on the USB speed. Uncommenting this line should force things to be the same as you've tested with those other libs.

Especially if the bigger buffers do not work, but this does, I really need to know. Not sure what I can do, but at least know there may be some other issue at 480 speed besides the buffer sizes.
 
If you uncomment this line in USBHost_t36.h, you'll get tons of debugging info in the Arduino Serial Monitor.

Code:
//#define USBHOST_PRINT_DEBUG

Maybe this can at least show whether the device is finishing enumeration and if the raw communication is happening?
 
Have you plugged any other USB devices, like a USB keyboard or mouse, into your Teensy 3.6 - just to make sure the USB host cable really is connected properly and working?

Any chance you can show photos and more specific info about exactly the hardware you're using? I read this thread again and clicked the link from msg #1, but all I find is obviously wrong info (something using USB host, not USB device).

Maybe I can help if I know better what you're really connecting, and if you post a capture of that excessive debug info.

Or if I could somehow get the same hardware here, I could try to reproduce the problem. But the info in this thread is very confusing.
 
Hi Paul,

I connected three different MIDI Keyboards to the Teensy and everything worked fine. They all have in common that they are powered through the USB connection. I used the exact same cable for all devices, so I'm quite sure the physical connection is OK. I could monitor MIDI traffic using the InputFunctions example that comes with the library with these three devices. Only the THR10 remains silent. Nethertheless, I will make a test with a mouse and a computer keyboard, too, and I will also activate Debugging output, as you pointed out, and will post the results here. I won't have time before tomorrow, though.
 
Good to know your hardware is working.

I still don't really know what a THR10 really is. Google search keeps turning up this Yamaha guitar amp.

https://www.amazon.com/Yamaha-THR10-Amplifier-Interface-Recording/dp/B006QLW5XC

But at least from the photos I can find, I don't see any USB ports. Is it on the back or bottom?

If the debug printing doesn't give useful results, then I'm afraid the only other way forward is if I can get my hands on one of these for a couple hours....
 
It's a DSP modelling amp that has a number of parameters available via a Windows/MacOS app that turned out to use sysex to communicate with the device so third parties reverse engineered the messages used via an Andriod app but it may not be compliant on the USB side, reports vary from what I've seen.

https://en.m.wikipedia.org/wiki/Yamaha_THR5/THR10_series

@Hirnilein0815... does the amp show as a MIDI device in any DAW or utility like MIDI OX?
 
This is the debug output:

USB Host InputFunctions example
sizeof Device = 36
sizeof Pipe = 96
sizeof Transfer = 64
power up USBHS PHY

-- Here I plugged in the THR10

port change: 11001803
connect
port change: 1D001002
disconnect
port change: 11001803
connect
port change: 1D001002
disconnect
port change: 11001803
connect
begin reset
port change: 11001005
port enabled
end recovery
new_Device: 12 Mbit/sec
new_Pipe
enumeration:
enumeration:
enumeration:
Device Descriptor:
12 01 00 02 FF 00 FF 40 99 04 07 15 00 01 01 02 00 01
VendorID = 0499, ProductID = 1507, Version = 0100
Class/Subclass/Protocol = 255 / 0 / 255
Number of Configurations = 1
enumeration:
enumeration:
Manufacturer: Yamaha Corporation
enumeration:
Product: THR10
enumeration:
Config data length = 54
enumeration:
Configuration Descriptor:
09 02 36 00 01 01 00 C0 00
NumInterfaces = 1
ConfigurationValue = 1
09 04 03 00 02 FF 03 FF 00
Interface = 3
Number of endpoints = 2
Class/Subclass/Protocol = 255 / 3 / 255
07 24 01 00 01 24 00
06 24 02 02 01 00
09 24 03 02 01 01 01 01 00
07 05 01 02 40 00 00
Endpoint = 1 OUT
Type = Bulk
Max Size = 64
Polling Interval = 0
07 05 82 02 40 00 00
Endpoint = 2 IN
Type = Bulk
Max Size = 64
Polling Interval = 0
enumeration:
USBHub memory usage = 960
USBHub claim_device this=1FFF4AE0
USBHub memory usage = 960
USBHub claim_device this=1FFF4EA0
Descriptor 4 = INTERFACE
MIDIDevice claim this=1FFF2020
len = 45
Descriptor 36 = ???
Descriptor 36 = ???
Descriptor 36 = ???
Descriptor 5 = ENDPOINT
Descriptor 5 = ENDPOINT


Oh, the Yamaha THR10 is a practice guitar amp that is fully MIDI programmable. As I said, I can communicate with the THR10 from an Arduino. But the Teensy is my preferred platform because I ran out of memory on the Arduino (I'm also using the SD Card Reader and an OLED display, so lots of libraries, not to forget my own code).
 
From the debug data, looks like the interface descriptor may be the problem.

09 04 03 00 02 FF 03 FF 00
Interface = 3
Number of endpoints = 2
Class/Subclass/Protocol = 255 / 3 / 255

The USB MIDI spec says the 6th byte (called "bInterfaceClass") is supposed to be 1, for audio class. But Yamaha put 255 in this byte!

As a quick & dirty fix, delete or comment out this line from midi.cpp:

Code:
        if (p[5] != 1) return false; // bInterfaceClass: 1 = Audio class

The downside to this edit is the MIDIDevice driver might try to claim non-MIDI interfaces from any other class (HID, mass storage, printers, etc) if the 7th byte happens to be 3.
 
The USB Host Shield library USB Host MIDI driver does the same check for class and subclass but if it fails, it looks for Bulk In and Bulk Out endpoints. If they exist, it uses them. This is far from ideal but it deals with non-class compliant MIDI devices.
 
This is far from ideal but it deals with non-class compliant MIDI devices.

How does it avoid falsely detecting non-MIDI interfaces?

Any chance you can point me to the specific lines in the code? (will save me a little time, since I'm not very familiar with the driver parts of those libraries)
 
Back
Top