Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple MIDI Devices through Hub #29

Open
ghost opened this issue Apr 4, 2017 · 44 comments
Open

Multiple MIDI Devices through Hub #29

ghost opened this issue Apr 4, 2017 · 44 comments
Assignees

Comments

@ghost
Copy link

ghost commented Apr 4, 2017

Again, sorry if this is a known issue, but I cannot find another issue discussing it.

I have modified the USB_MIDI_Dump demo to use two hubs and two midi devices.
I find that a single device works well through a hub, but creating a second midi device stops it working. It compiles, but doesn't connect to or receive data from either MIDI device once more than one is declared.

Many thanks,

Here's the code:

#define LOAD_USB_HOST_SYSTEM
#define LOAD_USB_HOST_SHIELD
#define LOAD_UHS_HUB
#define LOAD_UHS_MIDI
#define LOAD_UHS_PRINTF_HELPER
#define USB_HOST_SERIAL Serial

#include <Arduino.h>
#ifdef true
#undef true
#endif
#ifdef false
#undef false
#endif

#include <UHS_host.h>

MAX3421E_HOST UsbHost;
UHS_MIDI Midi(&UsbHost);
UHS_MIDI Midi2(&UsbHost);
UHS_USBHub Hub1(&UsbHost);
UHS_USBHub Hub2(&UsbHost);
bool connected;
char buf[20];


void setup() {
  // USB data switcher, PC -> device. (test jig, this can be ignored for regular use)
  pinMode(5, OUTPUT);
  digitalWrite(5, HIGH);
  connected = false;
  USB_HOST_SERIAL.begin(115200);
  while (UsbHost.Init(1000) != 0);
  printf_P(PSTR("\r\nHost initialized.\r\n"));

}

void loop() {
  if (Midi.isReady()) {
    if (!connected) {
      connected = true;
      printf_P(PSTR("Connected to MIDI\r\n"));
      sprintf(buf, "VID:%04X, PID:%04X", Midi.vid, Midi.pid);
      Serial.println(buf);
    }
    MIDI_poll();
  } else {
    if (connected) {
      connected = false;
      printf_P(PSTR("\r\nDisconnected from MIDI\r\n"));
    }
  }

}

// Poll USB MIDI Controler and send to serial MIDI
void MIDI_poll()
{
  uint8_t bufMidi[64];
  uint16_t  rcvd;
  if (Midi.RecvData( &rcvd,  bufMidi) == 0 ) {
    sprintf(buf, "%08lX: ", (uint32_t)millis());
    Serial.print(buf);
    Serial.print(rcvd);
    Serial.print(':');
    for (int i = 0; i < 64; i++) {
      sprintf(buf, " %02X", bufMidi[i]);
      Serial.print(buf);
    }
    Serial.println("");
  }
}

@YuuichiAkagawa
Copy link
Contributor

You must handle all instances.

#define LOAD_USB_HOST_SYSTEM
#define LOAD_USB_HOST_SHIELD
#define LOAD_UHS_HUB
#define LOAD_UHS_MIDI
#define LOAD_UHS_PRINTF_HELPER
#define USB_HOST_SERIAL Serial

#include <Arduino.h>
#ifdef true
#undef true
#endif
#ifdef false
#undef false
#endif

#include <UHS_host.h>

MAX3421E_HOST UsbHost;
UHS_MIDI Midi(&UsbHost);
UHS_MIDI Midi2(&UsbHost);
UHS_USBHub Hub1(&UsbHost);
UHS_USBHub Hub2(&UsbHost);
bool connected[2] = {false, false};
char buf[20];


void setup() {
  // USB data switcher, PC -> device. (test jig, this can be ignored for regular use)
  pinMode(5, OUTPUT);
  digitalWrite(5, HIGH);
//  connected = false;
  USB_HOST_SERIAL.begin(115200);
  while (UsbHost.Init(1000) != 0);
  printf_P(PSTR("\r\nHost initialized.\r\n"));

}

void loop() {
  if (Midi.isReady()) {
    if (!connected[0]) {
      connected[0] = true;
      printf_P(PSTR("Connected to MIDI\r\n"));
      sprintf(buf, "VID:%04X, PID:%04X", Midi.vid, Midi.pid);
      Serial.println(buf);
    }
    MIDI_poll(Midi);
  } else {
    if (connected[0]) {
      connected[0] = false;
      printf_P(PSTR("\r\nDisconnected from MIDI\r\n"));
    }
  }
  if (Midi2.isReady()) {
    if (!connected[1]) {
      connected[1] = true;
      printf_P(PSTR("Connected to MIDI\r\n"));
      sprintf(buf, "VID:%04X, PID:%04X", Midi.vid, Midi.pid);
      Serial.println(buf);
    }
    MIDI_poll(Midi2);
  } else {
    if (connected[1]) {
      connected[1] = false;
      printf_P(PSTR("\r\nDisconnected from MIDI\r\n"));
    }
  }
}

// Poll USB MIDI Controler and send to serial MIDI
void MIDI_poll(UHS_MIDI &m)
{
  uint8_t bufMidi[64];
  uint16_t  rcvd;
  if (m.RecvData( &rcvd,  bufMidi) == 0 ) {
    sprintf(buf, "%08lX: ", (uint32_t)millis());
    Serial.print(buf);
    Serial.print(rcvd);
    Serial.print(':');
    for (int i = 0; i < 64; i++) {
      sprintf(buf, " %02X", bufMidi[i]);
      Serial.print(buf);
    }
    Serial.println("");
  }
}

I have not tried the Poll() feature.

@ghost
Copy link
Author

ghost commented Apr 5, 2017

Ok, so you MUST use isReady() and RecvData() on all UHS_MIDI objects in order for any of them to work? That's the behaviour I'm getting.

Also, your above code works when a device is connected directly, but doesn't work on a hub for me, where as the code below does (a single midi device) :

#define LOAD_USB_HOST_SYSTEM
#define LOAD_USB_HOST_SHIELD
#define LOAD_UHS_HUB
#define LOAD_UHS_MIDI
#define LOAD_UHS_PRINTF_HELPER
#define USB_HOST_SERIAL Serial

#include <Arduino.h>
#ifdef true
#undef true
#endif
#ifdef false
#undef false
#endif

#include <UHS_host.h>

MAX3421E_HOST UsbHost;
UHS_MIDI Midi(&UsbHost);
UHS_USBHub Hub1(&UsbHost);
UHS_USBHub Hub2(&UsbHost);

bool connected;
char buf[20];


void setup() {
  // USB data switcher, PC -> device. (test jig, this can be ignored for regular use)
  pinMode(5, OUTPUT);
  digitalWrite(5, HIGH);
  connected = false;
  USB_HOST_SERIAL.begin(115200);
  while (UsbHost.Init(1000) != 0);
  printf_P(PSTR("\r\nHost initialized.\r\n"));

}

void loop() {
  if (Midi.isReady()) {
    if (!connected) {
      connected = true;
      printf_P(PSTR("Connected to MIDI\r\n"));
      sprintf(buf, "VID:%04X, PID:%04X", Midi.vid, Midi.pid);
      Serial.println(buf);

    }
    MIDI_poll();
  } else {
    if (connected) {
      connected = false;
      printf_P(PSTR("\r\nDisconnected from MIDI\r\n"));
    }
  }
}

// Poll USB MIDI Controler and send to serial MIDI
void MIDI_poll()
{
  uint8_t bufMidi[64];
  uint16_t  rcvd;
  if (Midi.RecvData( &rcvd,  bufMidi) == 0 ) {
    sprintf(buf, "%08lX: ", (uint32_t)millis());
    Serial.print(buf);
    Serial.print(rcvd);
    Serial.print(':');
    for (int i = 0; i < 64; i++) {
      sprintf(buf, " %02X", bufMidi[i]);
      Serial.print(buf);
    }
    Serial.println("");
  }
}

@YuuichiAkagawa
Copy link
Contributor

@xxxajk Help me. I don't know how to use with HUB.

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 5, 2017

1: Hubs take care of themselves.
2: yes you must poll each instance for isReady()

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 6, 2017

Oh btw, you can do this:

printf_P(PSTR("VID:%04X, PID:%04X"), Midi.vid, Midi.pid);

...and you can throw away the buf variable instead of this:

      printf_P(PSTR("Connected to MIDI\r\n"));
      sprintf(buf, "VID:%04X, PID:%04X", Midi.vid, Midi.pid);
      Serial.println(buf);

@YuuichiAkagawa
Copy link
Contributor

I think that it is not MIDI but HUB issue.

@mattomatto
I was test with 4 port HUB. It's work.
Is your HUB a D-link's 7 port? Can you try with another HUB?

@xxxajk
Can you provide HUB examples like UHS2's hub_demo?
We want to know how many HUB instances are needed.

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 7, 2017

Could be a bug, but all you should need to do is specify 2 hub instances, and it should work. I'll check on it later this morning.

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 9, 2017

What platform are you using? ARM or AVR, and which ARM or AVR?

@YuuichiAkagawa
Copy link
Contributor

YuuichiAkagawa commented Apr 9, 2017

I am using AVR. @mattomatto is ARM.
But I think that this issue depends on HUB hardware. It is the same situation in UHS 2.

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 9, 2017

Which AVR? Note that the ones with smaller amount of RAM might be the issue.
Sometimes even a MEGA1280 or MEGA2560 will have problems and can need external RAM in order to operate in a more complex mode. This is because each interface driver will consume RAM, and it is all dynamically allocated. If you need help with adding more RAM,let me know. I've done it before.
Sometimes you can end up with malloc (heap) arena fragmentation, which will mess with it too.
If you suspect either of these are the problem, use an MCU with more RAM, or simply don't expect a more complex set of devices connected. Another work around is to disable the printf helper, and avoid using printf entirely. This is because printf can cause memory fragmentation too. Even sprintf will cause this.

@PaulStoffregen
Copy link
Contributor

Will this code run on Teensy 3.x?

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 10, 2017

Yes :-)

@PaulStoffregen
Copy link
Contributor

@YuuichiAkagawa - email me directly (paul at pjrc dot com) with your mailing address and I'll send you a couple Teensy 3.x boards to help with testing this.

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 10, 2017

To be more clear on my quick answer:
1: as posted, the code will work on ANY teensy using the mini host shield.
2: If using native USB, you need to tell the code to do that in the sketch.
#define LOAD_UHS_KINETIS_FS_HOST
instead of
#define LOAD_USB_HOST_SHIELD

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 10, 2017

@YuuichiAkagawa email me directly: (xxxajk at gmail dot com) and I'll get to you a mega2560, a UHS mini, and a special edition of the USB Host Shield that I made, which is designed to be attached to the bottom of any MCU you want permanently, or used as a regular pluggable one. It is also UHS30 ready. I'll even do the rework on the mini so it can attach to the teensy 3.x. (heck, why not!)

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 10, 2017

@PaulStoffregen Note he will need to have a host cable. I made mine with a relay on it so I can always leave everything plugged in, and I set a pin to flip it to use the host cable. Email me an let me know if you want a schematic. I believe it would be a good product for you to sell. It lowers the wear and tare on the teensy 3.x's USB port, since you don't need to swap cables all the time. You just push the program button and then upload.

@PaulStoffregen
Copy link
Contributor

Maybe we can talk Kris (aka Onehorse) into making & selling these special cables on Tindie?

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 10, 2017

actually, it could just be made into a board.

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 10, 2017

If you are willing to sell it, I can actually produce these...

@PaulStoffregen
Copy link
Contributor

PJRC probably won't make and sell this. We probably could make more money if I created dozens more products, but it's also a huge distraction from my main mission of improving the Teensy platform and Arduino software & libraries in general.

My goal is to assist others to make & sell these sorts of add-on boards. Sometime "soon" (perhaps many months away) I'm going to redesign the website to showcase other products alongside Teensy.... things like this.

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 10, 2017

No rush here. I'll throw together something for him in the meantime. It won't be a professionally etched board, but done here in my lab.

@PaulStoffregen
Copy link
Contributor

Ok, I sent Kris a detailed message. Dunno if he'll be interested in making a board.

I need to update my own page about how to use the UHS library, since it's all old info about Teensy2 and pre-dates UHS3. Would be really nice to update it with photos and info about how to actually make this special cable connection.

If Kris (or anyone else) doesn't make & sell an add-on board, maybe you could send me a quick photo of your custom cable?

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 10, 2017

I'll email you, watch your inbox.

@ghost
Copy link
Author

ghost commented Apr 10, 2017

@YuuichiAkagawa Thanks for testing - I still can't get mine to work correctly. I've tried a 7 port D-link hub, a 4 port D-link hub, and a 4 port amazon hub. I'll try another brand.

@xxxajk I will try avoiding printf and test by forwarding MIDI data between devices, thanks.

@YuuichiAkagawa
Copy link
Contributor

Hmm.
Arduino UNO + UHS rev2.0 is work.

  • Arduino IDE 1.8.2(Windows)
  • 4 port USB HUB
  • AKAI MPK mini, KORG nanoKEY, nanoKONTROL

I test with Teensy 3.2 + UHS mini. It did not work with USB HUB.

  • Arduino IDE 1.8.2(Windows) + Teensyduino 1.36
  • CPU Speed: 72MHz
  • Optimize: Fast
  • 4 port USB HUB
  • AKAI MPK mini, KORG nanoKEY, nanoKONTROL

PID error occurred repeatedly.
uhs30-teensy-hub-failed

Also TEST_BULK did not work too.

I will try Kinetis native tomorrow.

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 10, 2017

Interesting. I'll have to hook up my beagle as well and see what I can find out here on Saturday too.

@YuuichiAkagawa
Copy link
Contributor

YuuichiAkagawa commented Apr 11, 2017

I tested it with native host. Did not work via HUB. Direct connect is OK.

@xxxajk
Can you teach me your teensy setting?
e.g. compile option, Arduino IDE and Teensyduino version.

#define NATIVEHOST 1
#define LOAD_USB_HOST_SYSTEM
#ifdef NATIVEHOST
#define LOAD_UHS_KINETIS_FS_HOST
#else
#define LOAD_USB_HOST_SHIELD
#endif
#define LOAD_UHS_HUB
#define LOAD_UHS_MIDI
#define LOAD_UHS_PRINTF_HELPER
#ifdef NATIVEHOST
#define USB_HOST_SERIAL Serial1
#else
#define USB_HOST_SERIAL Serial
#endif

#include <Arduino.h>
#ifdef true
#undef true
#endif
#ifdef false
#undef false
#endif

#include <UHS_host.h>

#ifdef NATIVEHOST
UHS_KINETIS_FS_HOST UsbHost;
#else
MAX3421E_HOST UsbHost;
#endif
UHS_USBHub Hub1(&UsbHost);
UHS_USBHub Hub2(&UsbHost);
UHS_MIDI Midi(&UsbHost);
UHS_MIDI Midi2(&UsbHost);
bool connected[2] = {false, false};


void setup() {
  // USB data switcher, PC -> device. (test jig, this can be ignored for regular use)
  pinMode(5, OUTPUT);
  digitalWrite(5, HIGH);
//  connected = false;
  USB_HOST_SERIAL.begin(115200);
  while (UsbHost.Init(1000) != 0);
  printf_P(PSTR("\r\nHost initialized.\r\n"));

}

void loop() {
  if (Midi.isReady()) {
    if (!connected[0]) {
      connected[0] = true;
      printf_P(PSTR("Connected to MIDI\r\n"));
      printf_P(PSTR("VID:%04X, PID:%04X"), Midi.vid, Midi.pid);
    }
    MIDI_poll(Midi);
  } else {
    if (connected[0]) {
      connected[0] = false;
      printf_P(PSTR("\r\nDisconnected from MIDI\r\n"));
    }
  }
  if (Midi2.isReady()) {
    if (!connected[1]) {
      connected[1] = true;
      printf_P(PSTR("Connected to MIDI\r\n"));
      printf_P(PSTR("VID:%04X, PID:%04X"), Midi2.vid, Midi2.pid);
    }
    MIDI_poll(Midi2);
  } else {
    if (connected[1]) {
      connected[1] = false;
      printf_P(PSTR("\r\nDisconnected from MIDI\r\n"));
    }
  }
}

// Poll USB MIDI Controler and send to serial MIDI
void MIDI_poll(UHS_MIDI &m)
{
  uint8_t bufMidi[64];
  char buf[20];
  uint16_t  rcvd;
  if (m.RecvData( &rcvd,  bufMidi) == 0 ) {
    sprintf(buf, "%08lX: ", (uint32_t)millis());
    USB_HOST_SERIAL.print(buf);
    USB_HOST_SERIAL.print(rcvd);
    USB_HOST_SERIAL.print(':');
    for (int i = 0; i < 64; i++) {
      sprintf(buf, " %02X", bufMidi[i]);
      USB_HOST_SERIAL.print(buf);
    }
    USB_HOST_SERIAL.println("");
  }
}

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 11, 2017

That sketch should work fine.
I suspect some kind of difference between AVR and ARM.
Paul uses a different compiler, so perhaps compiler bug that needs to be accommodated for, or I am simply missing something else.
Does it work with the mini shield?
Does it work on DUE?

If yes to only the second, then compiler is suspected.

@PaulStoffregen
Copy link
Contributor

Unless you've selected LTO in Tools > Optimize, it's very unlikely to be the compiler.

LTO optimization has exposed numerous bugs. So far, all appear to be in the libraries, not the compiler. But plenty of less than ideal practices exist in my libs, so LTO is not the default for ARM yet...

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 11, 2017

This could also be related to another problem I have ran into.
Specifically the problem of not enough ram bandwidth.
Currently that is processed as a NAK, which could be the incorrect way for me to handle it.
@PaulStoffregen little bit of help/clue as how to handle that situation would be very appreciated.

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 11, 2017

@PaulStoffregen
Copy link
Contributor

little bit of help/clue as how to handle that situation would be very appreciated.

Without a reproducible test case, the best I can suggest is fiddling with the bus switch matrix priority settings. But I'd recommend against going down that path. Your time would be much better spent on isolating the issue and coming up with a test case that reproduces the problem.

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 11, 2017

That's easy to do, run the file system program, and after the first time (there should be success) replug the thumb drive and you should end up seeing it. The possible correct way according to the DS and PRG is to resend it. What it doesn't say is if you have to change the toggle bits, and it doesn't say if there are any other caveats, such as success on the other end, you may be doing the same thing twice, which isn't good.

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 12, 2017

Here is where the error is actually translated. See comment in code.

if(rcode == UHS_HOST_ERROR_MEM_LAT) {
// something we need to do here, but what?
// datasheet isn't clear, just says that these are transient
rcode = UHS_HOST_ERROR_NAK;
break;
}

As far as playing with the AXBS/MCM_CR it looks to me that the backdoor is never set and is in the default mode.

I'll bet you dollars to doughnuts that if SRAM_[UL] is set to:
11 Fixed priority. Backdoor has highest, processor has lowest
the problem disappears. I would think this would be the best-case anyway?
I am also surprised that this would not have been the case for DMA.
Any thoughts?
I'll try this on Saturday myself and let you know the impact.

@YuuichiAkagawa
Copy link
Contributor

YuuichiAkagawa commented Apr 13, 2017

I test with DUE + UHS mini. Did not work via HUB. Direct connect is OK.

  • Arduino IDE 1.8.2(Windows)
  • 4 port USB HUB
  • AKAI MPK mini, KORG nanoKEY, nanoKONTROL

DUE's compiler is

AppData\Local\Arduino15\packages\arduino\tools\arm-none-eabi-gcc\4.8.3-2014q1/bin/arm-none-eabi-g++ --version
arm-none-eabi-g++ (GNU Tools for ARM Embedded Processors) 4.8.3 20140228 (release) [ARM/embedded-4_8-branch revision 208322]

Teensy's compiler is

arduino-1.8.2\hardware\teensy/../tools/arm/bin/arm-none-eabi-gcc --version
arm-none-eabi-gcc (GNU Tools for ARM Embedded Processors) 5.4.1 20160919 (release) [ARM/embedded-5-branch revision 240496]

PID error occurred repeatedly.
uhs30-due-hub-failed

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 13, 2017 via email

@YuuichiAkagawa
Copy link
Contributor

Yes. AVR is works.

But, if I connect the USB HUB and the MIDI device first and then turn on the power, it will take a very long time until it works.

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 13, 2017 via email

@ghost
Copy link
Author

ghost commented Apr 18, 2017

Hi guys,

Just catching up on this issue - am I correct in thinking that there is a confirmed issue on ARM with multiple MIDI instances through a hub?

@xxxajk
Copy link
Collaborator

xxxajk commented Apr 18, 2017 via email

@ghost
Copy link
Author

ghost commented Apr 19, 2017

Great, thanks.

@ghost
Copy link
Author

ghost commented Jul 21, 2017

Hi all,

I've been checking in regularly on this and have not seen any movement. I've just tried the latest version of the library and this is still an issue for me. I can't get more than one Midi device working through a hub.

Would it be possible for someone to look into this again?

Many thanks,

Matt

@xxxajk
Copy link
Collaborator

xxxajk commented Feb 16, 2018

Try latest that I pushed today. I have not had the chance to test it, but by looking at the code there was a very obvious bug in the polling timer.

Not sure if it fixes ARM, but could!
I'll be doing testing later this morning myself, and there are probabbly a few other horrible bugs lurking in the hub code.

@xxxajk
Copy link
Collaborator

xxxajk commented Aug 1, 2018

Please confirm this is now working. I will be doing a push fairly soon. If no comment after a few days, and if it does work for me, I will assume this has been fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants