Pages: [1] 2 3   Go Down
Author Topic: Arduino dropping midi signals  (Read 8120 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 11
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm trying to make an Arduino circuit to accept signals from a midi source (in this case a midi keyboard) and pass (most of) them back out for a little pet project.

The order of devices goes:
keyboard -> Arduino -> commercial midi to USB converter
I'm using kuk's midi-in circuit from here, hooked to the Rx pin of my Arduino Uno.
The midi out is coming from my Uno's Tx pin.

Now, my problem is... When monitoring the midi signals coming in from the midi to USB converter on my wife's Mac (using a program called MIDI monitor), it seems to be dropping signals. It will only register a set of note on/note off commands every 4 seconds or so. If I go any faster than that, the monitor doesn't even see the command. But clock signals and active sense signals seem to stream in no problem.

I am really at a loss here. My baud rate is 31250 as per the spec. At first I thought my optocoupler was too slow (4n28) so I bought 3 other kinds and they all have the same behavior, even though running them through a test circuit verified that the optocoupler itself was working correctly. Then I thought the Arduino MIDI library I was using was flawed, so I tried using straight serial writing. Now I am reading about how the Arduino introduces some serial latency, and I am worried that it means the Arduino Uno is too slow to do what I want to do.

Any help at all would be appreciated. I can show you the code I am using, I can (try to) show my circuit(s), I can show you the whole setup from keyboard to converter. Just... somebody please help me before I go crazy!
Logged

0
Offline Offline
Faraday Member
**
Karma: 16
Posts: 2855
ruggedcircuits.com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

OK, show us everything smiley   One thing I noticed from the schematic you reference is that the 3.3k resistor is too high. It needs to be about 10 times smaller (330 ohms or so) else the rising and falling edges will be too slow.

We've got a Flexible MIDI Shield product that might simplify your life a little bit (and clean up your wiring). If you look on that page there is also some sample code that works for sure (on a Mac even!). There is really no reason for a 4 second delay....

--
Beat707: MIDI drum machine / sequencer / groove-box for Arduino
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 11
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok, so, this is one version of the code I've been working on

With the Arduino MIDI library from the playground:
Code:
#include <Compatibility_v2.5.h>
#include <MIDI.h>

const byte CHANNEL = 0x01;
const int numReadings = 10;

int readings[numReadings];      // the readings from the analog inputint[] averages;
int index = 0;                  // the index of the current reading
int total = 0;                  // the running total
unsigned int average = 0;                // the average
int analogInputPin = A5;

void setup() {
  pinMode(13,OUTPUT);
  //initialize array for averaging
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }
  MIDI.begin(CHANNEL);
  digitalWrite(13,HIGH);
  MIDI.turnThruOff();
}

void loop() {
  //Read midi input
  //if input {
  if (MIDI.read() && (MIDI.getType()==0x80 || MIDI.getType()==0x90)) {
    // pass it on thru
    byte b1 = MIDI.getData1();
    byte b2 = MIDI.getData2();
    if (MIDI.getType()==0x90) {
        MIDI.sendNoteOn(b1,b2,CHANNEL);
    } else {
        MIDI.sendNoteOff(b1,b2,CHANNEL);
    }
  } else {
  
    // get pedal value
    unsigned int pedalVal = analogRead(analogInputPin);

    //average this reading with the last 10 readings
    averageReadings(pedalVal);
    
    // sendPitchBend(pedalVal,CHANNEL);
    MIDI.sendPitchBend(average, CHANNEL);
    
  }
  
}

void averageReadings(int newVal) {
    // subtract the last reading:
  total= total - readings[index];        
  // read from the sensor:  
  readings[index] = newVal;
  // add the reading to the total:
  total= total + readings[index];      
  // advance to the next position in the array:  
  index = index + 1;                    

  // if we're at the end of the array...
  if (index >= numReadings)              
    // ...wrap around to the beginning:
    index = 0;                          

  // calculate the average:
  average = total / numReadings;    
}

Other code forthcoming. Schematics/current setup coming as time allows.
« Last Edit: June 20, 2011, 10:16:59 pm by hek8981 » Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 11
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code I found online that did straight serial writes that looked like what I wanted (mostly. Had to modify some things):
Code:
/* Midi In/out/merge (based on midiin Basic  0.2 // kuki 8.2007) modified OlivarPremier 5.2008
 *
 * -----------------
 * listen for midi serial data, and light leds for individual notes + send midi + merge midi from potentiometer
 
 IMPORTANT:
the optocoupler is powered by pin 10
 

HARDWARE NOTE:
The Midi Socket is connected to arduino RX through an opto-isolator to invert the midi signal and seperate the circuits of individual instruments.
connect 8 leds to pin2-pin9 on your arduino.

####################################################################################################################################################
 
 */

//variables setup

byte incomingByte = 255;
byte note = 255;
byte velocity = 255;
byte noteonch = 0;
byte noteoffch = 0;
byte controlch = 0; //byte for control change message ch 1-16
byte control = 255;   //byte for control change number
byte controlval = 0;//byte for control change value
byte programch = 0; //byte for program change message ch 1-16
byte programval = 0;//byte for program change value
byte aftertch = 0;//byte for channel aftertouch
byte aftertval = 0;//byte for channel aftertouch value
byte pitchbch = 0;//byte for channel pitch bend ch1-16
byte pitchbMSB = 255;//byte 1 for pitch bend value
byte pitchbLSB = 255;//byte 2 for pitch bend value

int pot1 = 0;
byte pot1n = 0;

int statusLed = 13;   // select the pin for the LED

int action=2; //0 =note off ; 1=note on ; 2= nada; 3=control change; 4=programchange, 5=channel aftertouch, 6=pitchbend

//setup: declaring iputs and outputs and begin serial
void setup() {
  pinMode(statusLed,OUTPUT);   // declare the LED's pin as output

  pinMode(10,OUTPUT);
 
  //start serial with midi baudrate 31250 or 38400 for debugging
  Serial.begin(31250);       
  digitalWrite(statusLed,HIGH);
  digitalWrite(10, HIGH);

  //Not sure what this is about -hek
  for(int i=176; i<=191; i++)
  {
  Serial.print(i, BYTE);
  Serial.print(123, BYTE);
  }
 
}

//loop: wait for serial data, and interpret the message or let it pass thru as is and merge message from pot on analogin0
void loop () {
  //send data from arduino
  pot1=analogRead(0);
  pot1=pot1/8;

  //midi merging starts here !
  if (Serial.available() > 0) {
    // read the incoming byte:
    incomingByte = Serial.read();
    //Serial.print(incomingByte); //debuggin purpose

    // wait for timing clock
    if ( (incomingByte== 248) || (incomingByte== 250)||(incomingByte== 251) || (incomingByte==252)){ // clock messages (24=timing, 250=start, 251=continue, 252=stop)
      Serial.print(incomingByte, BYTE);   
      pitchbch = 224;
      action=2;
    }
    // wait for song position
    if (incomingByte== 242){ // song position message
        pitchbch = 224;
      Serial.print(242, BYTE);
      Serial.print(Serial.read());
      Serial.print(Serial.read());
      action=2;
    }
    // wait for as status-byte, channel 1, note on or off
    if ( (incomingByte>= 144) && (incomingByte<=159)){ // note on message starting starting
     noteonch = incomingByte;
     action=1;
     note=255;
    }
    else if (incomingByte>= 128 && incomingByte<=143){ // note off message starting
      noteoffch = incomingByte;
      action=0;
      note=255;
    }
    else if (incomingByte>= 160 && incomingByte<=175){ // message is key aftertouch
    pitchbch = 224+(incomingByte-160);
    action=2; //nothing happens
    }
    else if (incomingByte>= 176 && incomingByte<=191){ // message is control change
    pitchbch = 224+(incomingByte-176);
    controlch=incomingByte;
    action=3; //control change
    control=255; 
    }
    else if (incomingByte>= 192 && incomingByte<=207){ // message is program change
    pitchbch = 224+(incomingByte-192);
    programch=incomingByte;
    action=4; //control change 
    }
    else if (incomingByte>= 208 && incomingByte<=223){ // message is channel aftertouch
    pitchbch = 224+(incomingByte-208);
    aftertch=incomingByte;
    action=5; //channel aftertouch 
    }
    else if (incomingByte>= 224 && incomingByte<=239){ // message is pitch bend
    pitchbch=incomingByte;
    action=6; //pitch bend
    pitchbMSB=255;
    }
    else if (incomingByte>= 240 && incomingByte<=255){ // message is sysex (240) active sense(254)
    pitchbch = 224+(incomingByte-208);
    action=2; //do nothing 
    }
    //process second byte
     
    else if ( (incomingByte<=127) && (action ==1 || action==0)&&(note==255) ){ // if we received note number message load this byte in the note byte
      note=incomingByte;
    }
    else if ( (incomingByte<=127) && (action ==3)&&(control==255) ){ // if we received controlchange message load this byte in the control number byte
     control=incomingByte;
    }
    else if ( (incomingByte<=127) && (action ==4) ){ // if we received program change message load this byte in the control number byte
     programval=incomingByte;
     //twoBmidiMsg(programch, programval);
     midiMsg(pitchbch, pot1, pot1);
    }
    else if ( (incomingByte<=127) && (action ==5) ){ // if we received program change message load this byte in the control number byte
     aftertval=incomingByte;
     //twoBmidiMsg(aftertch, aftertval);
     midiMsg(pitchbch, pot1, pot1);
    }
    else if ( (incomingByte<=127) && (action ==6)&&(pitchbMSB==255) ){ // if we received pitchbend message load this byte in the MSB byte
     pitchbMSB=incomingByte;
    }
    //process Third byte
   
    else if ( (incomingByte<=127) && (action ==0)&&(note!=255) ){ // if we received note message load this byte in the velocity byte
     velocity=0;
     playNote(note, velocity);
     note=255;
    }
    else if ( (incomingByte<=127) && (action ==1)&&(note!=255) ){ // if we received noteon message load this byte in the velocity byte
     velocity=incomingByte;
     playNote(note, velocity);
     note=255;
    }
    else if ( (incomingByte<=127) && (action ==3)&&(control!=255) ){ // if we received controlchange message load this byte in the control value byte
     controlval=incomingByte;
     //midiMsg(controlch, control, controlval);
     midiMsg(pitchbch, pot1, pot1);
     control=255;
     }
     else if ( (incomingByte<=127) && (action ==6)&&(pitchbMSB!=255) ){ // if we received pitchbend message load this byte in the LSB byte
     pitchbLSB=incomingByte;
     midiMsg(pitchbch, pitchbMSB, pitchbLSB);
     pitchbLSB=255;
    }
    else{
      //nada
     
    }
  }
//Serial.print(incomingByte, BYTE);
}


void playNote(byte note, byte velocity){
  int value=LOW;
  if (velocity >10){
      value=HIGH;
  }else{
   value=LOW;
  }
 
 if(action==0){
  Serial.print(noteoffch, BYTE);
  Serial.print(note, BYTE);
  Serial.print(velocity, BYTE);
  }
  else if(action==1){
  Serial.print(noteonch, BYTE);
  Serial.print(note, BYTE);
  Serial.print(velocity, BYTE);
  }
  else {}
   
 /* don't need this -hek
 //since we don't want to "play" all notes we wait for a note between 36 & 44
 if(note>=36 && note<44 && (noteonch==144 || noteoffch==128)){ //led command on ch1 only
   byte myPin=note-34; // to get a pinnumber between 2 and 9
   digitalWrite(myPin, value);
 //send midi message back
 
 }
 */
 }

// Send a 2 byte MIDI message
void twoBmidiMsg(byte cmd, byte data1) {
  Serial.print(cmd, BYTE);
  Serial.print(data1, BYTE);
}
// Send a 3 byte MIDI message
void midiMsg(byte cmd, byte data1, byte data2) {
  Serial.print(cmd, BYTE);
  Serial.print(data1, BYTE);
  Serial.print(data2, BYTE);
}
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 11
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ultimately, the point is, if the keyboard sends a note (on or off), send the note through to the midi out. Any other signal, trap it and send out a pitch bend signal, with the pitch bend value being read from an analog input.

OK, show us everything smiley   One thing I noticed from the schematic you reference is that the 3.3k resistor is too high. It needs to be about 10 times smaller (330 ohms or so) else the rising and falling edges will be too slow.
Well that certainly made my ears prick up... Too slow? I don't have a 330 ohm resistor, but I have 220s and 100s to put in series. Close enough, right?

We've got a Flexible MIDI Shield product that might simplify your life a little bit (and clean up your wiring). If you look on that page there is also some sample code that works for sure (on a Mac even!). There is really no reason for a 4 second delay....

--
Beat707: MIDI drum machine / sequencer / groove-box for Arduino
I would really like to avoid buying a shield for MIDI if possible. I mean, kinda the point of this project was to do it myself... Just a stubbornness I have. But thank you for the suggestion. If push comes to shove, and a head of hair turns into fistfuls of it, I'll definitely keep your shield in mind. Thank you for your help so far.
« Last Edit: June 20, 2011, 10:18:44 pm by hek8981 » Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 11
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sorry to machine gun posts like this (probably violating a board etiquette thing, and for that I am sorry), but is there a preferred method for showing one's schematic/circuit? I mean, I could take a picture and post it, but that's not going to be all that easy to see...
Logged

0
Offline Offline
Faraday Member
**
Karma: 16
Posts: 2855
ruggedcircuits.com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Well that certainly made my ears prick up... Too slow? I don't have a 330 ohm resistor, but I have 220s and 100s to put in series. Close enough, right?

Right. You could even just use a 220 by itself.

The code is....a bit overwhelming smiley  I don't know where to start looking for issues. It would be best to trim things down to as simple a sketch as possible and see if the problem is hardware or software.

--
The Ruggeduino: compatible with Arduino UNO, 24V operation, all I/O's fused and protected
Logged

0
Offline Offline
Faraday Member
**
Karma: 16
Posts: 2855
ruggedcircuits.com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
is there a preferred method for showing one's schematic/circuit?

It would be best to indeed post a *schematic*, not a wiring diagram or a picture of your breadboard. It's very hard to figure out what your design intentions are from those. The original 4N28 circuit you referenced was pretty close to a schematic (it would have been better if it showed the internal LED and transistor). Take a look at the schematic of the shield I mentioned (or any other Arduino shield for that matter) and see if you can sketch something similar on paper.

Then start learning Eagle smiley

--
The Gadget Shield: accelerometer, RGB LED, IR transmit/receive, speaker, microphone, light sensor, potentiometer, pushbuttons
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 11
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This is my midi in and my midi out (assuming I drew it all correctly and followed my circuit exactly). The second batch of code calls for pin 5 of the 4N35 to be ultimately hooked to D10 of the Arduino, so that's where it goes when I work with that code.


*edit* Whoops. Forgot the pin 1 indicator on the 4N35


As for simple, lesser code sketches, I can do midi out no problem. I can programmatically write any signal I want. Midi IN, however, I have NEVER gotten to work. It always has this dropped signal/4 second lag problem
« Last Edit: September 17, 2011, 02:40:52 pm by hek8981 » Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 11
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Simplest sketch there could ever be for midi:
Code:
void setup() {
  pinMode(13,OUTPUT); //"on" LED setup
  Serial.begin(31250);
  digitalWrite(13,HIGH); //turn "on" LED on
}

void loop() {
   if (Serial.available()>0)  {
      Serial.write(Serial.read());
   }
}
It seems to pass the messages through ok for a bit, but then seems to get out of sync, and the midi monitor program starts seeing invalid messages, pitch wheel messages, and LOTS of stop messages. Any remedy for this? Isn't the whole point of giving them the same baud rate to keep them in lock-step? Do I need to power the opto via a digital pin so I turn it on at the same time the Arduino finishes its setup?

Also, putting the 220 Ohm resistor in place of the 3.3k as suggested got me NOTHING coming in, so I went back to the 3.3k.
Logged

0
Offline Offline
Faraday Member
**
Karma: 16
Posts: 2855
ruggedcircuits.com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
[Also, putting the 220 Ohm resistor in place of the 3.3k as suggested got me NOTHING coming in, so I went back to the 3.3k.

Now that's interesting. Makes me think the 4N28 is not the right opto here, or you need to play with that resistor. 220 may indeed be too small for the 4N28 yet 3.3k too high. I think it's worth trying something in the middle.

--
The Rugged Motor Driver: two H-bridges, more power than an L298, fully protected
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 11
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Well, I had to ditch the 4n28 because it failed the opto test circuit found here. I guess it went bad or something. Using a 4N35 instead because after the 28 failed I went back to the parts store (no returns! all sales final!) and said "Ok, give me one of every optocoupler you've got." Happened to test the 35 first, it passed, so I stuck it in the circuit. Pinout's the same. Can't see too much difference in the properties of it but admittedly, I am pretty noob-y with this stuff. As if that wasn't already obvious! Anyway, datasheets for each:

4N28:
http://www.datasheetcatalog.com/datasheets_pdf/4/N/2/8/4N28.shtml
4N35:
http://pdf1.alldatasheet.com/datasheet-pdf/view/2848/MOTOROLA/4N35.html

If you could take a look and see if I am underestimating the differences, I would appreciate that.

In the meantime, I am going to monkey around with the other resistors I have on hand to see if I can find a combination that puts me in a "sweet spot"

Thanks for all your help and advice so far
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 11
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

REALLY stupid question time:

When writing midi messages, should you transmit all the bytes at once in a single loop:
Code:
void loop() {
  Serial.write(commandbyte);
  Serial.write(data1);
  Serial.write(data2);
}

or should it transmit one byte per go-round:
Code:
void loop() {
  if (messageStep==0) { //first step, first byte
    Serial.write(commandbyte);
  } elseif (messageStep==1){ //second step, second byte (if needed)
    Serial.write(data1);
  } else { //last step, 3rd byte (if needed), then reset to first step
    Serial.write(data2);
    messageStep=-1; //The ++ later will make it 0
  }
  messageStep++; //increment which step we are on
}
? (that's not real code. That's just illustrating my question.)

Because I've seen it one way sometimes, and the other way other times, and I can't find anything definitive...
« Last Edit: September 17, 2011, 03:09:32 pm by hek8981 » Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This brings up a question I've been wondering about-- Serial.write is apparently, not buffered nor asynchronous.    And as far as I can tell, there is no Serial.write_available function that will return the value of the Data Register Empty flag in the USART.    Consequently, if you are writing out more data than you are reading in, things can back up on the input side.   I do see that the HardwareSerial functions do include some RX buffering, either 32 or 128 bytes per USART depending on available memory, and there does seem to be RX interrupts used.

I'm planning to do a lot of serious MIDI processing as well, and it's clear to me that a more robust asynchronous Serial.write mechanism is in order.    I'm planning on doing some merging operations on an Arduino mega where MIDI messages coming from two input sources get merged into a single output.    This could benefit from some output buffering.    No matter what, given a sufficiently dense stream of MIDI input from two sources funnelling into one, an overrun can occur-- but buffering could reduce the likelyhood so that an overrun won't occur with "typical" use (whatever that is).

Looking at HardwareSerial.cpp, I see that the write function boils down to:

void HardwareSerial::write(uint8_t c)
{
  while (!((*_ucsra) & (1 << _udre)));
  *_udr = c;
}

So it clearly blocks until the data register is empty.    A function designed to check this first could be useful, particularly in cases where input MIDI message might produce multiple output messages, such as in the case of some of the demos where an input Note ON translates to two on the output side.   Such a function can then be used to create serial output buffering externally to the HardwareSerial functions as needed.

At the moment, I'm not sure how the "_ucsra" pointer gets set up, it's apparently passed into the HardwareSerial instance when it's created?   


--

Zinc

Logged

0
Offline Offline
Faraday Member
**
Karma: 16
Posts: 2855
ruggedcircuits.com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I believe the beta version of the Arduino GUI includes interrupt-driven transmission of characters as well, not just reception. So it might be worth to just download that and start playing with it:

https://github.com/arduino/Arduino/blob/new-extension/hardware/arduino/cores/arduino/HardwareSerial.cpp

--
The Rugged Circuits Yellowjacket: 802.11 WiFi module with ATmega328P microcontroller, only 1.6" x 1.2", bootloader
Logged

Pages: [1] 2 3   Go Up
Jump to: