USB Host Library MIDI hanging

tyzjames

Well-known member
I'm testing a basic code sending MIDI via usbHost on a Teensy 4.1 (lets call this the controller).

Code:
// Simple test of USB Host
//
// This example is in the public domain

#include "USBHost_t36.h"

USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub3(myusb);
KeyboardController keyboard1(myusb);
KeyboardController keyboard2(myusb);
MIDIDevice midi1(myusb);

void setup()
{
    while (!Serial) ; // wait for Arduino Serial Monitor
    Serial.println("USB Host Testing");
    myusb.begin();
}


void loop()
{
    myusb.Task();
    midi1.read();
  midi1.send(0xB0, 0, 0, 5);
  delay(1);
  Serial.print(millis());
  Serial.println(" send");
}

I have the host port connected to another MIDI device (lets call this device B).

Device B runs usbMIDI.read() in a loop. Once Device B gets put into a loop where it does not run usbMIDI.read(), the controller hangs. I believe the controller is waiting for a response from the USB Device when it sends a MIDI message.

I've narrowed it down the the usbHost code not removing the transfer from the async followup list (remove_from_async_followup_list).

Is there any way I can prevent the controller from stalling if it does not receive a response from the USB device?
 
After further troubleshooting, I have narrowed it down to this code being stuck in a loop in this function in midi.cpp, where tx2 == tx_max == 16, and hence it was not matching any of the conditions.

Code:
void MIDIDeviceBase::write_packed(uint32_t data)
{
    if (!txpipe) return;
    uint32_t tx_max = tx_size / 4;

    while (1) {
        __disable_irq();
        uint32_t tx1 = tx1_count;
        uint32_t tx2 = tx2_count;

        print("tx2: "); println(tx2);
        print("tx_max: "); println(tx_max);
        if (tx1 < tx_max && (tx2 == 0 || tx2 >= tx_max)) {
            // use tx_buffer1
            tx_buffer1[tx1++] = data;
            tx1_count = tx1;
            txtimer.stop();
            if (tx1 >= tx_max) {
                queue_Data_Transfer(txpipe, tx_buffer1, tx_max*4, this);
            } else {
                txtimer.start(tx_max >= 128 ? 200 : 1500);
            }
            __enable_irq();
            return;
        }
        if (tx2 < tx_max) {
            // use tx_buffer2
            tx_buffer2[tx2++] = data;
            tx2_count = tx2;
            txtimer.stop();
            if (tx2 >= tx_max) {
                queue_Data_Transfer(txpipe, tx_buffer2, tx_max*4, this);
            } else {
                txtimer.start(tx_max >= 128 ? 200 : 1500);
            }
            __enable_irq();
            return;
        }
        __enable_irq();
        // TODO: call yield() ??
    }
}


I just changed the divisor from 4 to 2, and that seems to resolve the issue.
 
Just did more tests and unfortunately, the above solution does not work when sending SysEx messages from the USBHost.

The case where tx2 >= tx_max && tx1 >= tx_max is not handled in the function above, which leads to an infinite loop. Hopefully someone has an idea on how to resolvec this.
 
Did you resolve this? I wonder if this issue explains why I can't get the Basic example in USBHost_t36 to work. I have "Serial + MIDI" in the Tools menu selected. My MIDI monitor sees the Teensy MIDI device, but it doesn't see any messages. I added the code below to the Loop to get it to send a pair of notes in one second intervals, but so far, nothing.

digitalWrite(LED_BUILTIN, HIGH);
midi1.sendNoteOn(42, 127, 1); // Send a Note (pitch 42, velo 127 on channel 1)
delay(1000); // Wait for a second
midi1.sendNoteOff(42, 0, 1); // Stop the note
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
digitalWrite(LED_BUILTIN, HIGH);
midi1.sendNoteOn(58, 127, 1); // Send a Note (pitch 42, velo 127 on channel 1)
delay(1000); // Wait for a second
midi1.sendNoteOff(58, 0, 1); // Stop the note
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
 
I just discovered that I was using the wrong MIDI library. Calls to usbMIDI work fine, at least so far.
Teensy / USB_MIDI / *
which library are you now using? I am having similar problems with usb midi over usb host. recieves fine, but as soon as any midi is sent it hangs. It doesnt reset so i am thinking its stuck in a loop.
 
but as soon as any midi is sent it hangs.

Improvements to USB host MIDI transmit were recently made. Can you try with the latest 1.59-beta5? If using Arduino IDE 2.2.x, very easy to try. First close the small Teensy Loader window (if it is open), then in Boards Manager use the drop-down list to install 0.59.5.

If that doesn't solve your problem, can you show a complete program and specify which USB MIDI device(s) to connect to reproduce the problem? If it really is a problem with the library, the only way I can investigate is with a program that reproduces the issue.
 
Last edited:
Improvements to USB host MIDI transmit were recently made. Can you try with the latest 1.59-beta5? If using Arduino IDE 2.2.x, very easy to try. First close the small Teensy Loader window (if it is open), then in Boards Manager use the drop-down list to install 0.59.5.

If that doesn't solve your problem, can you show a complete program and specify which USB MIDI device(s) to connect to reproduce the problem? If it really is a problem with the library, the only way I can investigate is with a program that reproduces the issue.
Hi @PaulStoffregen
I will give the beta 1.59 a go. I should clarify that the hang usually happens after a few attempts to send midi over the USB host. My program is huge, and im not going to publish it anytime soon, however I suspect my problems might be low memory:
Code:
Memory Usage on Teensy 4.1:
  FLASH: code:426360, data:161756, headers:8872   free for files:7529476
   RAM1: variables:120800, code:376648, padding:16568   free for local variables:10272
   RAM2: variables:22784  free for malloc/new:501504
 EXTRAM: variables:1116928
I am using USB midi on the client connection, 4 cables, 2x serial midi and am trying to get USB host midi to work with a hub. I haven't found the examples to be much help re USB host midi, but am struggling through.


Here is my declaration: (with many failed versions commented)


C++:
//-------------------------------------------------------------------------  midi usb host
/*
//USB HOST MIDI Class Compliant
USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub3(myusb);
USBHub hub4(myusb);
//MIDIDevice midi1(myusb);
MIDIDevice usbMIDIhost1(myusb);
MIDIDevice usbMIDIhost2(myusb);
MIDIDevice usbMIDIhost3(myusb);
MIDIDevice usbMIDIhost4(myusb);

MIDIDevice_BigBuffer midi1(myusb); // Try this if your MIDI Compliant controller has problems
*/


//
//USBHost myusb;
//USBHub usbHostHubs[4] = {USBHub(myusb), USBHub(myusb), USBHub(myusb), USBHub(myusb)};
//
//MIDIDevice usbMIDIhost[4] = {usbHostHubs[0].(myusb), usbHostHubs[1].(myusb), usbHostHubs[2].(myusb), usbHostHubs[3].(myusb)};
//
//MIDIDevice_BigBuffer midiDevices[4] = {MIDIDevice_BigBuffer(myusb), MIDIDevice_BigBuffer(myusb), MIDIDevice_BigBuffer(myusb), MIDIDevice_BigBuffer(myusb)};




USBHost myusb;
//USBHub usbHostHubs[4] = {USBHub(myusb), USBHub(myusb), USBHub(myusb), USBHub(myusb)};
USBHub hub1(myusb);
MIDIDevice_BigBuffer usbMIDIhost[4] = {
  MIDIDevice_BigBuffer(myusb),
  MIDIDevice_BigBuffer(myusb),
  MIDIDevice_BigBuffer(myusb),
  MIDIDevice_BigBuffer(myusb)
};




//USBHost myusb;
//USBHub usbHostHubs[4] = {USBHub(myusb), USBHub(myusb), USBHub(myusb), USBHub(myusb)};
//
//MIDIDevice usbMIDIhost[4] = {
//  MIDIDevice(usbHostHubs[0]),
//  MIDIDevice(usbHostHubs[1]),
//  MIDIDevice(usbHostHubs[2]),
//  MIDIDevice(usbHostHubs[3])
//};

/*
// Create the ports for USB devices plugged into Teensy's 2nd USB port (via hubs)
USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub3(myusb);
USBHub hub4(myusb);
MIDIDevice midi01(myusb);
MIDIDevice midi02(myusb);
MIDIDevice midi03(myusb);
MIDIDevice midi04(myusb);
//MIDIDevice midi05(myusb);
//MIDIDevice midi06(myusb);
//MIDIDevice midi07(myusb);
//MIDIDevice midi08(myusb);
//MIDIDevice midi09(myusb);
//MIDIDevice midi10(myusb);
MIDIDevice * usbMIDIhost[4] = {
  &midi01, &midi02, &midi03, &midi04  //, &midi05, &midi06, &midi07, &midi08, &midi09, &midi10
};
*/



//MIDIDevice_BigBuffer midiDevices[4] = {
//  MIDIDevice_BigBuffer(myusb),
//  MIDIDevice_BigBuffer(myusb),
//  MIDIDevice_BigBuffer(myusb),
//  MIDIDevice_BigBuffer(myusb)
//};

Today has been spent moving some functions to FLASHMEM in an effort to free up some memory. i will report back on 1.59 once i have installed it. currently using 1.58 on arduino 1.8.16
 
The improvement that went into 1.59-beta mostly addresses the scenario where you transmit host MIDI to more than 1 device at the same time or very nearly the same time.
 
Last edited:
@PaulStoffregen upgraded to 1.59. the problem persists but I noticed it only happens when certain devices are attached via USB.
I have a simple 4 port hub attached to the t4.1 usb host port. During my development over the past year or so I have had an alesis v25 keyboard attached and this has worked fine. I have been working on multiple USB host midi objects, and since the new year i have been experiencing this hanging issue. Today i noticed that the issue presents itself only with some midi devices connected and not others. Notably a behringer crave. even when its just connected, sending note off, a clock message it seems to send a few messages, then crashes. I have narrowed down with someSerial.print debug. I wonder if its the attached device sending a reply message? Is there any method i can use to enable debug messages in the USB host lib or the midi library? Before i spend time to try to create a cut down sketch that I can share to help identify the issue, I think its best I try to locate the issue here first.
 
Since you have not posted your entire sketch, only guesses are possible. One thing that one of my radio club buddies has encountered when building his homemade teensy-based sequencer is that, even though the sequencer had essentially no need to receive anything from any other MIDI device, it was absolutely critical to have each interface (traditional serial MIDI, usbMIDI, & USBhostMIDI) to always read (& effectively discard) any incoming MIDI messages, otherwise the MIDI receive queue on one of the interfaces would fill (best guess) & the device would appear to hang.

For devices defined as follows:

Code:
USBHost thisUSB;
USBHub hub1(thisUSB);
MIDIDevice_BigBuffer usbhostMIDI(thisUSB);

MIDI_CREATE_DEFAULT_INSTANCE();

Make sure that you have the following in your loop() function:

Code:
   MIDI.read();
   usbMIDI.read();
   thisUSB.Task();
   usbhostMIDI.read();

Hope that helps . . .

Mark J Culross
KD5RXT
 
Yes, I managed to fix this.

The issue is that the code is getting stuck in the while loop in the write_packed function.

Here is my fix:
void MIDIDeviceBase::write_packed(uint32_t data)
{
if (!txpipe) return;
uint32_t tx_max = tx_size / 4;
while (1) {
__disable_irq();
uint32_t tx1 = tx1_count;
uint32_t tx2 = tx2_count;
if (tx1 < tx_max && (tx2 == 0 || tx2 >= tx_max)) {
// use tx_buffer1
tx_buffer1[tx1++] = data;
tx1_count = tx1;
txtimer.stop();
if (tx1 >= tx_max) {
queue_Data_Transfer(txpipe, tx_buffer1, tx_max*4, this);
} else {
txtimer.start(tx_max >= 128 ? 200 : 1500);
}
__enable_irq();
return;
}
if (tx2 < tx_max) {
// use tx_buffer2
tx_buffer2[tx2++] = data;
tx2_count = tx2;
txtimer.stop();
if (tx2 >= tx_max) {
queue_Data_Transfer(txpipe, tx_buffer2, tx_max*4, this);
} else {
txtimer.start(tx_max >= 128 ? 200 : 1500);
}
__enable_irq();
return;
}
else if (tx2 >= tx_max) {
__enable_irq();
return;
}

__enable_irq();
// TODO: call yield() ??
// yield();
}
}

I added a condition so the code can exit the loop
else if (tx2 >= tx_max) {
__enable_irq();
return;
}


Then I determined that the issue was due to the SysEx messages being sent too quickly in the send_sysex_add_term_bytes function

void MIDIDeviceBase::send_sysex_add_term_bytes(const uint8_t *data, uint32_t length, uint8_t cable)
{
cable = (cable & 0x0F) << 4;
if (length == 0) {
write_packed(0x06 | cable | (0xF0 << 8) | (0xF7 << 16));
return;
} else if (length == 1) {
write_packed(0x07 | cable | (0xF0 << 8) | (data[0] << 16) | (0xF7 << 24));
return;
} else {
write_packed(0x04 | cable | (0xF0 << 8) | (data[0] << 16) | (data[1] << 24));
data += 2;
length -= 2;
}
uint32_t m = micros();
while (length >= 3) {
write_packed(0x04 | cable | (data[0] << 8) | (data[1] << 16) | (data[2] << 24));

while (micros() - m < 400);
m = micros();
data += 3;
length -= 3;
}
if (length == 2) {
write_packed(0x07 | cable | (data[0] << 8) | (data[1] << 16) | (0xF7 << 24));
} else if (length == 1) {
write_packed(0x06 | cable | (data[0] << 8) | (0xF7 << 16));
} else {
write_packed(0x05 | cable | (0xF7 << 8));
}
}

So I added a 400 microsecond delay between each write_packed function call here
while (micros() - m < 400);
m = micros();

This fixed all the hanging issues I was facing related to the USBHost MIDI functions
 
It looks like this is related to the problem I'm having:

I have a Teensy 4.1 and am trying to use a USB-MIDI keyboard as INPUT to the Teensy 4.1 using the USB host header and connector. I also don't see +5V on the USB host header; not sure if that's expected or not.

I've never had the MIDI working at all, but even if I just add `USBHost_t36.h` and `-D USB_MIDI` and `Serial.print` stops working. Take them out, it starts working again. It's kinda hard to debug with nothing but a blinking LED. Am I missing something basic here?

Earlier on this thread Paul said:
You can edit USBHost_t36.h to enable printing debug messages.
What would that edit look like? I've tried defining `USB_MIDI_SERIAL` at the very top of main.cpp but it still doesn't work.

My goal is to do USB-MIDI on the USB Host port of Teensy 4.1 while still maintaining the ability to AT LEAST do printf debugging on the standard USB port.

Thanks for any help.

Regards,
Ian
 
Don't use -D USB_MIDI, that's for making the Teensy function as a USB device which isn't needed as a host..
Start with the USBHost_t36 examples and work from there, since they're known working code (you won't see 5V on the USB port until it's turned on).
 
Did you look at the USBHost_t36 examples ?? If you look at the USBHost_t36 / MIDI / Basic example (see the following screen capture on how to get there):

1706312707170.png


From this, you'll see that you need to define your USBhost interface as follows (NOTE: the USBHub lines are only necessary if you plan to connect a USB hub to the USBhost interface):

Code:
#include "USBHost_t36.h"

USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub3(myusb);

In addition, you'll also see the following in the setup() function (this is most critical because this is what actually turns on the 5VDC to the USBhost interface):

Code:
    myusb.begin();

Without actually seeing the entirety of your sketch, this is purely a guess . . .

Mark J Culross
KD5RXT
 
Oh, don't worry. My sketch is literally just "receive midi message, blink LED" at this point. Thank you very much for your help!
 
Then I determined that the issue was due to the SysEx messages being sent too quickly in the send_sysex_add_term_bytes function

Have you tried the latest beta? In Arduino IDE 2.2.1, just use Boards Manager to install Teensyduino 0.59.5.

An timing issue was recently fixed involving USB host MIDI transmit. Maybe you were experiencing that issue?
 
Oh, don't worry. My sketch is literally just "receive midi message, blink LED" at this point. Thank you very much for your help!
OK, simple enough, but did you include the call to myusb.begin(); in your setup() function ?? You made the comment "I also don't see +5V on the USB host header; not sure if that's expected or not." in your earlier post, which is what my reply was attempting to assist with. Without the necessary call to enable the USBhost interface, it is absolutely expected not to see +5VDC on the USBhost header. Again, without the ability to see your sketch (as simple as you may think it is), we're simply guessing.

Hope that helps . . .

Mark J Culross
KD5RXT
 
I've gotten the basics working. Starting from the "Teensy > USBHost_t36 > MIDI > Basic" example sketch, I had to remove the references to keyboards to get the MIDI part working. I'm not sure why, but the MIDI parts started working once I did. I'm also attempting to use PlatformIO instead of Arduino IDE (because the Arduino IDE is hot garbage, IMHO). When things don't work, I fall back to Arduino IDE just to be sure it's not my IDE or build system, but I might be having issues that other people aren't having.

As of now, my code looks like this:
C++:
#include "USBHost_t36.h"
#include "EEPROM.h"

USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub3(myusb);
// KeyboardController keyboard1(myusb);
// KeyboardController keyboard2(myusb);
MIDIDevice midi1(myusb);

void OnPress(int key);
void OnRawPress(uint8_t keycode);
void OnRawRelease(uint8_t keycode);
void OnNoteOn(byte channel, byte note, byte velocity);
void OnNoteOff(byte channel, byte note, byte velocity);
void OnControlChange(byte channel, byte control, byte value);

void setup()
{
  while (!Serial)
    ; // wait for Arduino Serial Monitor
  Serial.println("USB Host Testing");
  myusb.begin();
  // keyboard1.attachPress(OnPress);
  // keyboard1.attachRawPress(OnRawPress);
  // keyboard1.attachRawRelease(OnRawRelease);
  // keyboard2.attachPress(OnPress);
  midi1.setHandleNoteOff(OnNoteOff);
  midi1.setHandleNoteOn(OnNoteOn);
  midi1.setHandleControlChange(OnControlChange);
}

void loop()
{
  myusb.Task();
  midi1.read();
  // Serial.println("tick");
}

void OnPress(int key)
{
  Serial.print("key '");
  Serial.print((char)key);
  Serial.print("'  ");
  Serial.println(key);
  // Serial.print("key ");
  // Serial.print((char)keyboard1.getKey());
  // Serial.print("  ");
  // Serial.print((char)keyboard2.getKey());
  // Serial.println();
}

void OnRawPress(uint8_t keycode)
{
  Serial.print("raw key press: ");
  Serial.println((int)keycode);
}

void OnRawRelease(uint8_t keycode)
{
  Serial.print("raw key release: ");
  Serial.println((int)keycode);
}

void OnNoteOn(byte channel, byte note, byte velocity)
{
  Serial.print("Note On, ch=");
  Serial.print(channel);
  Serial.print(", note=");
  Serial.print(note);
  Serial.print(", velocity=");
  Serial.print(velocity);
  Serial.println();
}

void OnNoteOff(byte channel, byte note, byte velocity)
{
  Serial.print("Note Off, ch=");
  Serial.print(channel);
  Serial.print(", note=");
  Serial.print(note);
  // Serial.print(", velocity=");
  // Serial.print(velocity);
  Serial.println();
}

void OnControlChange(byte channel, byte control, byte value)
{
  Serial.print("Control Change, ch=");
  Serial.print(channel);
  Serial.print(", control=");
  Serial.print(control);
  Serial.print(", value=");
  Serial.print(value);
  Serial.println();
}

I don't really have a question at this exact time, but I figured I'd share in case anyone else runs into this stuff.

Regards,
Ian
 
I have a similar problem. I narrowed it down to hostMidi.begin() causing a hard freeze a second or two later after a MIDI device or even a mouse is connected. loop() is no more called and even reprogramming does not work unless the device is disconnected and then Teensy is reset.

Here is the sketch:

Code:
#include <USBHost_t36.h>
#include <MIDI.h>

USBHost hostUsb;
USBHub hub1(hostUsb);
USBHub hub2(hostUsb);
MIDIDevice_BigBuffer hostMidi(hostUsb);
MIDI_CREATE_DEFAULT_INSTANCE();
int note = 0;

void setup() {
  while(!Serial);
  delay(500);
  hostUsb.begin();
  hostMidi.setHandleNoteOn(handleNoteOn);
  hostMidi.setHandleNoteOff(handleNoteOff);
  while(!hostMidi);
  hostMidi.begin();
}

void handleNoteOn(midi::DataByte note, midi::DataByte pressure, midi::Channel channel) {
  ++note;
}

void handleNoteOff(midi::DataByte note, midi::DataByte pressure, midi::Channel channel) {
}

void loop() {
  hostUsb.Task();
  hostMidi.read();
  Serial.print(note);
  delay(100);
}

After a MIDI device is connected, the code prints a few zeroes, then Teensy halts hard. I thought that maybe I soldered something incorrectly and tried with another Teensy. The same problem. Both Teensies work correctly with the USB host/Mouse example. Connecting a mouse prints its name, deconnecting prints a respective message and nothing freezes.

When I remove while(!hostMidi); in the above sketch, zeroes are printed before anything is connected without a freeze. Yet when I connect either a MIDI device or a mouse (the same which is correctly recognized by the host mouse example), Teensy freezes a second later.

Commenting out MIDI_CREATE_DEFAULT_INSTANCE() does not seem to make any difference.

Arduino 2.3.2, latest Teensyduino. I attached the compilation log.
 

Attachments

  • teensy_host_midi.txt
    34.4 KB · Views: 242
I defined USBHOST_PRINT_DEBUG in USBHost_t36/midi.cpp (required the removal of the two print_hexbytes inside) and that's what gets printed before the freeze:
Code:
MIDIDevice claim this=20002220
len = 150
MIDIDevice claim this=20002220
len = 115
MIDIDevice claim this=20002220
len = 92
  Interface is unknown (might be Yahama)
type: 36, len: 9
    MIDI Header (ignored)
type: 4, len: 9
MIDIDevice claim this=20002220
len = 74
  Interface is MIDI
type: 36, len: 7
    MIDI Header (ignored)
type: 36, len: 6
    MIDI IN Jack (ignored)
type: 36, len: 6
    MIDI IN Jack (ignored)
type: 36, len: 9
    MIDI OUT Jack (ignored)
type: 36, len: 9
    MIDI OUT Jack (ignored)
type: 5, len: 9
    MIDI Endpoint: 2
      tx_size = 64
type: 37, len: 5
    MIDI Endpoint Jack Association (ignored)
type: 5, len: 9
    MIDI Endpoint: 83
      rx_size = 64
type: 37, len: 5
    MIDI Endpoint Jack Association (ignored)

However, nothing is printed by USBHost_t36/midi.cpp after removing while(!hostMidi); and then connecting a mouse even if, as said, it also freezes Teensy up to not responding to the loader.
 
I made Teensy stuck with its USB host and then suspended an Ubuntu system to which Teensy was connected. An hour later I tried to wake the system up. It woke up, then apparently freezed. So I reset the computer. BIOS was booting up longer than usual and when Ubuntu finally took control, it got stuck at
Code:
usb 5-1: Device descriptor read/64, error -110
It looped through this until I disconnected the (still?) stuck Teensy.

By the way, downgrading to Teensy 1.56.2 does not help.
 
I would suggest printing a hexdump of the descriptors returned by the midi device and manually decoding them, the USBHost code is likely getting stuck in an infinite loop somewhere while trying to parse them.
 
USBHost or the MIDI driver? The USB host demo which uses multiple drivers to recognize different devices (but not the MIDI driver) did not hang after connecting a MIDI device. Also, the loop while(!hostMidi); did not hang when connecting one. Moreover, that loop was apparently correctly recognizing MIDI devices because it ended when one was connected.

All was ok until MIDI.begin() was called when a MIDI device was connected. That freezed asynchronously (a second after MIDI.begin() ended) the whole system.

This was a problem for me as it meant that I probably needed to look into some interrupt handlers but I do not know how the USB host stack works and in particular, how it interacts with the drivers. Maybe you could point me to functions where I could do some debugging? Do Serial.write() works from an interrupt? I seemed to have a problem with that.
 
Last edited:
Back
Top