felis / UHS30

For information about the project see README below
GNU General Public License v2.0
133 stars 39 forks source link

Multiple MIDI Devices through Hub #29

Open ghost opened 7 years ago

ghost commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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

xxxajk commented 7 years ago

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

xxxajk commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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

YuuichiAkagawa commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

Will this code run on Teensy 3.x?

xxxajk commented 7 years ago

Yes :-)

PaulStoffregen commented 7 years ago

@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 commented 7 years ago

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 commented 7 years ago

@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 commented 7 years ago

@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 commented 7 years ago

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

xxxajk commented 7 years ago

actually, it could just be made into a board.

xxxajk commented 7 years ago

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

PaulStoffregen commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

I'll email you, watch your inbox.

ghost commented 7 years ago

@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 commented 7 years ago

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

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

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

Also TEST_BULK did not work too.

I will try Kinetis native tomorrow.

xxxajk commented 7 years ago

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

YuuichiAkagawa commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

Link pointing out the problem:

https://github.com/felis/UHS30/blob/master/libraries/UHS_host/UHS_KINETIS_FS_HOST/UHS_KINETIS_FS_HOST_INLINE.h#L318-L324

PaulStoffregen commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

Here is where the error is actually translated. See comment in code. https://github.com/felis/UHS30/blob/0923cee2d76b266fad338341944880479e5ec4f3/libraries/UHS_host/UHS_KINETIS_FS_HOST/UHS_KINETIS_FS_HOST_INLINE.h#L621-L626

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 commented 7 years ago

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

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 commented 7 years ago

And this works on AVR, though, correct?

On Thu, Apr 13, 2017 at 9:33 AM, Yuuichi Akagawa notifications@github.com wrote:

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

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/felis/UHS30/issues/29#issuecomment-293896556, or mute the thread https://github.com/notifications/unsubscribe-auth/ADskzJSKoAtJQzxxlJQadK7IaqZ3EcZgks5rviQ0gaJpZM4My351 .

-- Visit my github for awesome Arduino code @ https://github.com/xxxajk

YuuichiAkagawa commented 7 years ago

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 commented 7 years ago

Because AVR is a bit slower, but ok, so it does work on AVR. That's a good clue. Thanks!

On Thu, Apr 13, 2017 at 11:08 AM, Yuuichi Akagawa notifications@github.com wrote:

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.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/felis/UHS30/issues/29#issuecomment-293923191, or mute the thread https://github.com/notifications/unsubscribe-auth/ADskzLI6_dTEbM6wiRQCSBleanQGpfRzks5rvjp6gaJpZM4My351 .

-- Visit my github for awesome Arduino code @ https://github.com/xxxajk

ghost commented 7 years ago

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 commented 7 years ago

Yeah, Still attempting to find out what the issue is. I was a bit busy this weekend with all sorts of holiday junk.

On Tue, Apr 18, 2017 at 12:14 PM, mattomatto notifications@github.com wrote:

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?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/felis/UHS30/issues/29#issuecomment-294896194, or mute the thread https://github.com/notifications/unsubscribe-auth/ADskzP_n8Vs9qdG5NhurUxonldHipubmks5rxOFSgaJpZM4My351 .

-- Visit my github for awesome Arduino code @ https://github.com/xxxajk

ghost commented 7 years ago

Great, thanks.

ghost commented 7 years ago

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 commented 6 years ago

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 commented 6 years ago

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.