FortySevenEffects / arduino_midi_library

MIDI for Arduino
MIT License
1.56k stars 253 forks source link

Using USB MIDI features with Wire library causes USB MIDI sends to hang #158

Closed SandwichRising closed 4 years ago

SandwichRising commented 4 years ago

Hey there, I've been trying to troubleshoot this without much success. I've been using this library along with your linked USB-MIDI and MIDIUSB libraries so that all my serial and USB MIDI has the same syntax. Everything works great until my Wire library accesses my external EEPROM, and upon using:
Wire.endTransmission(); and Wire.requestFrom(diskAddress,qtyBytes); the program starts hanging on every MIDI send out over USB. Also, the messages that are hanging never reach the destination. If I omit those lines of wire or omit USB MIDI send outs from my code the issue never happens.

I am working with a bare ATmega32U4 compiling as an "Arduino Leonardo" board in the tools menu.

franky47 commented 4 years ago

Sounds like it could be an interrupt issue, @lathoub what do you think ?

lathoub commented 4 years ago

That was also my first thought @franky47 - could be a collision between Wire and the underlying USB driver (PluggableUSB?) using the same interrupt? (USB transport and MIDIUSB do not use interrupts, so assume the underlying Arduino PluggableUSB)

SandwichRising commented 4 years ago

For some more information, swapping Wire out with softwareWire still has the same issue, and using only a single type of MIDI USB send-out will run for a few seconds before the issue starts.

For example: using only a .sendNoteOn, or .sendNoteOff, or .sendProgramChange individually and commenting out the others will send a few times before the hangs start happening, however using my code as intended with multiple USB send-outs in a single program loop will always hang quickly.

The hang lasts for about 0.25 seconds before the program proceeds (without actually transmitting the USB data).

Serial MIDI send outs over a pin-controlled DIN port always continue happening, no interruption or slowdown.

lathoub commented 4 years ago

can you provide the minimum code that causes the hang (I had a look at your Sundial code, but that was over 1600 lines of code)

SandwichRising commented 4 years ago

So, I cut the code way down, and I also built a breadboard pro micro with external 24lc256 EEPROM chip connected. I'm using the same code, just trimmed down to what seemed to be causing the issue, with both a large breadboard build using a bare ATmega32U4 and also the breadboard Pro Micro hooked up to the external EEPROM. Apparently it's more complicated than I originally thought, the pro micro DOES NOT hang. The breadboard bare 32U4 DOES hang. Also of note, the hung chip can still be immediately reprogrammed over USB without any problems.

Here's the cut down code, it just flipflops between midi note on and midi note off messages, and after a few seconds starts pulling from the external EEPROM. The breadboard only hangs after the EEPROM pulls start, sometimes very quickly, sometimes after 10-15 messages:

#include <Arduino.h>

//EEPROM Libraries
#include <Wire.h> //for saving to external chip eeprom

//MIDI Libraries
#include <MIDI.h>
#include <USB-MIDI.h> //companion to MIDI.h for usb midi usage

// -- MIDI USB + Serial Initialize -- //
// USB
USBMIDI_CREATE_INSTANCE(0,MIDIUSB)

unsigned long currentMillis = 0;  
unsigned long timer200ms = 0; 
bool canRun200ms = true;
int iterationCounter = 0;
bool flipFlopper = 0;

//external EEPROM I2C address
#define DISK1_ADDR 0x50  //24LC256 I2C address with all address pins wired LOW to GND

int trash = 0;

//-- SETUP --
void setup() {

  //Begin I2C 
  Wire.begin();

  //Begin MIDI 
  MIDIUSB.begin(MIDI_CHANNEL_OMNI);

}
//-- END OF SETUP --

//-- LOOP --
void loop() {

  timerHandler();

 if(canRun200ms) {
    iterationCounter += 1;    

    if (iterationCounter > 20) {
      trash = pullEepromByte(DISK1_ADDR,2,1);
    }
    if (flipFlopper) {
      MIDIUSB.sendNoteOn(trash,127,1);

    } else { 
      MIDIUSB.sendNoteOff(trash,0,1);
    }

    flipFlopper = !flipFlopper;
  }
}
//-- END OF LOOP --

int pullEepromByte(byte diskAddress, int byteAddress, byte qtyBytes) {
  int rdata = 0;
  Wire.beginTransmission(diskAddress);
  Wire.write((int)(byteAddress >> 8));   // MSB
  Wire.write((int)(byteAddress & 0xFF)); // LSB
  Wire.endTransmission();
  Wire.requestFrom(diskAddress,qtyBytes);
  rdata = Wire.read();
  return rdata;
}

void timerHandler() {
  currentMillis = millis();
  canRun200ms = 0;    
  if(currentMillis >= timer200ms) {
    timer200ms = currentMillis + 200;
    canRun200ms = 1;
  }
}
SandwichRising commented 4 years ago

I have disconnected everything from the bare ATmega32u4 setup except the EEPROM chip. So now, there is only a 16MHz clock, the capacitors for the clock, reset pullup resistor, UCAP capacitor for USB, AREF capacitor, and USB cable with resistors on the data line. Nothing else is hooked up to the bare chip, and it is still hanging on the code I posted. However, the pro micro runs fine on the same code with an EEPROM chip hooked up.

SandwichRising commented 4 years ago

I have another ATmega32U4 that is mounted to a PCB made for this device, so I went to that and uploaded the code above with SoftwareWire instead of Wire because the EEPROM is on different pins. There were no issues, no hanging. So, with my breadboarded chip I started moving around to different pins using SoftwareWire and it was working fine, no hangs. I can even go back to the SDA and SCL pins on the chip and it doesn't hang with SoftwareWire. However, when I switch over to Wire and use the hardware wire library, it starts hanging again on that breadboard setup. The Pro Micro never hangs, it works with both hardware and software wire. But the schematic for the pro micro doesn't show anything else attached to the SDA and SCL pins, my breadboard is setup the same.

I'm starting to think something is wrong with my microcontroller, but I don't have another bare chip to test with. I think I'm going to either order another one or take the one off the PCB to test more in the breadboard setup.

SandwichRising commented 4 years ago

While waiting for more ATmega32U4's to get shipped to me, I switched over to an SAMD21G18 chip by way of an Adafruit Isty Bitsy M0 express. Using the same code linked above and compiled using the Adafruit M0 libraries for Arduino IDE the device works great up until it accesses the EEPROM over Wire. As soon as it does that the chip begins hanging, however with the SAMD21, it is able to keep transmitting MIDI data over USB, it's just much much slower than it should be, with nearly 1.5 seconds between flipfloped on/off MIDI messages. Also of note, the SAMD21 always immediately begins hanging as soon as the EEPROM is accessed. It does not sometimes work for many iterations before hanging like the ATmega32U4 will.

So, I'm thinking it's not just an issued of a damaged ATmega32U4, but I don't know why it's not consistently breaking with a Pro Micro. I also tried to run the SAMD21 with SoftwareWire, however it won't compile with that library on that board, so I'm unable to test that.

SandwichRising commented 4 years ago

It looks like the SAMD21 hanging was caused by not having pullup resistors on the i2c line. I will put the ATmega32U4 setup back together as soon as I have time and see if this resolves it there as well. I have a feeling it will, I think the capacitance in the socket and breadboard was enough to overcome the internal pullups that Wire turns on, but in the pro micro it was less, and the internal pullups still functioned.