Arduino Uno erkennt MIDI-Signale nicht richtig

Hi Freunde,
ich habe ein Problem.

Natürlich möchte ich mich erstmal kurz vorstellen, da das hier mein allererster Post in diesem Forum ist. Ich bin Player X, ein Launchpadder aus YouTube, bin 26 Jahre alt, programmiere gerne und mache auch sehr gerne elektronische Musik und Launchpadprojekte.

Nun möchte ich gerne auch ein paar Games für Launchpads schreiben, aber das Problem dabei ist, dass der Arduino Uno die eingehenden MIDI-Signale leider nicht richtig erkennt. Zum Anfang habe ich erstmal ein Sketch zum Testen geschrieben, bei dem das vom Launchpad Pro gesendete Signal genau so vom Arduino Uno wieder zurückgesendet wird, wie es empfangen wurde (sozusagen MIDI Thru), sodass immer die gedrückten Tasten leuchten, aber das tuen sie leider nicht.

byte statusByte;
byte dataByte1;
byte dataByte2;

void setup() {
  pinMode(13,OUTPUT);
  Serial.begin(31250);
}

void loop() {
  if (Serial.available()) {
    statusByte = Serial.read();
    dataByte1 = Serial.read();
    dataByte2 = Serial.read();
    if (statusByte == 0x90) {digitalWrite(13,HIGH);}
    delay(10);
    Serial.write(statusByte);
    Serial.write(dataByte1);
    Serial.write(dataByte2);
  }
  digitalWrite(13,LOW);
}

Wenn man ein NoteOn-Signal sendet, müsste wie in diesem Video hier die eingebaute LED logischerweise auch kurz aufblitzen, aber die bleibt leider immer aus. Darum frage ich mich auch, wieso es im Video funktioniert. Hängt das etwa damit zusammen, dass er dabei einen Arduino Leonardo statt einen Arduino Uno benutzt? Ich hab's auch mal mit delays zwischen den Serial.reads probiert, falls die 3 Bytes der MIDI-Signale auch nicht schnell genug hintereinander gesendet werden und somit das Programm auch einfach so schon in die nächste Zeile springt.
Komisch an der ganzen Sache ist auch, dass der if-Teil bei einem NoteOn-Signal (0x90, 0x..., 0x7F) gleich 2 mal hintereinander vom Arduino Uno durchgeführt wird und bei einem NoteOff-Signal (0x90, 0x..., 0x00) nur einmal.
Leider hab' ich in dem Sketch für den Arduinoboy durch das Wirrwarr der ganzen Tabs die entsprechende mehr oder weniger hilfreiche Stelle zum Empfangen nicht gefunden (also wie es dort geschrieben wurde).

Kann mir vlt. jemand von euch helfen, wie ich das Sketch dementsprechend umschreiben muss, damit der Arduino Uno die eingehenden MIDI-Signale endlich mal richtig erkennt?

Vielen Dank schon mal im Voraus!

Vielleicht solltest Du besser testen auf

  Serial.available() >= 3

bevor Du 3 Bytes abholst.

Naja, tut mir leid. Ich hab's jetzt mal probiert, aber auch das hat leider nix geändert. Das ist genauso wie mit den delays zwischen den Serial.reads. Außerdem sendet das Launchpad Pro manchmal auch SysEx-Nachrichten, die viel mehr als nur 3 Bytes haben, und wenn die Anzahl dieser Bytes mal nicht durch 3 teilbar ist, kommt das ganze eben durcheinander, sodass das Statusbyte dann leider auch nicht mehr an 1. Stelle steht.
Und was mich jetzt noch viel mehr ankotzt: Abhängig davon, welche Note bzw. welcher Controller grade gesendet wird, wird die IF-Kette bei 2 MIDI-Signalen mit jeweils 3 Bytes (drücken und loslassen) entweder nur einmal durchlaufen oder 1 mal - 2 mal - 2 mal - 1 mal - 2 mal - 2 mal - ....

In welcher INO-Datei bzw. welchem Tab für den Arduinoboy stehen denn eig. die Befehle zum Empfangen?

Hi

Gegenfrage: Wie viele .ino Dateien hast Du denn?

Oder Anders: Zeige uns, was Du hast, dann zeigen wir Dir, was Da schief läuft.
Mit der Bitte, Code-Tags für Dich und Deinen Sketch zu entdecken.

MfG

Bei Arduinoboy kann ich Dir auch nicht weiterhelfen, mit MIDI habe ich keine Erfahrung.

postmaster-ino:
Hi

Gegenfrage: Wie viele .ino Dateien hast Du denn?

Oder Anders: Zeige uns, was Du hast, dann zeigen wir Dir, was Da schief läuft.
Mit der Bitte, Code-Tags für Dich und Deinen Sketch zu entdecken.

MfG

Naja, ein Arduinoboy ist ein Shield, mit dem man den Gameboy mit einem MIDI-Gerät verbinden kann und hier ist das Sketch dazu. Wo dort die Befehle zum Empfangen sind, weiß ich aus dem Grund nicht, weil ich's nämlich nicht selber gemacht habe. Das gibt's schon länger.
Allerdings wurde davon der Button auf einen anderen Pin versetzt, sodass das Umschalten in einen anderen Modus hiermit leider nicht mehr funktioniert. Das Sketch für den jetzigen Arduinoboy (jedenfalls der, den ich mir gekauft habe) gibt's auf der Herstellerseite, wobei die Tastaturfunktion leider fehlerhaft ist, sodass ich mir das Sketch direkt vom Hersteller geben lassen musste.

Aber wie auch immer. Mein Sketch wollte ich mir jedenfalls selber zusammenstellen, und dafür muss ich wissen, wie der Arduino Uno die MIDI-Signale richtig empfängt. Wichtig ist, dass MIDI eine Baudrate von 31250 hat.

DrDiettrich:
Bei Arduinoboy kann ich Dir auch nicht weiterhelfen, mit MIDI habe ich keine Erfahrung.

Ach, keine Sorge! Dann erklär' ich's dir einfach. MIDI ist nämlich soooo einfach, dass es sogar ein Baby verstehen kann.

Also, MIDI hat 16 Kanäle (durch das 2. Nibble des Statusbytes). Die meisten MIDI-Signale haben 3 Bytes, manche wie z.B. Program Change (Instrumentenwechsel) auch nur 2 Bytes, aber bei jedem 1. Byte ist das 128er-Bit immer an, damit das Byte ein Statusbyte ist. Dadurch fängt ein MIDI-Signal nämlich an. Das 1. Nibble bestimmt die Art des Signals (z.B. NoteOn, NoteOff oder Control Change) und das 2. Nibble den Kanal. Z.B. ist 0x94 NoteOn auf Kanal 5, 0x8F NoteOff auf Kanal 16 oder 0xB0 Control Change auf Kanal 1.
Danach kommen meistens 2 weitere Bytes (bei Manchen wie gesagt auch nur 1 weiteres Byte), bei denen das 128-Bit immer aus ist, denn sonst würde damit ein neues MIDI-Signal anfangen und das alte MIDI-Signal würde gar nicht zur Geltung kommen. Bei NoteOn bzw. NoteOff z.B. bestimmt das 2. Byte die Note und das 3. Byte die Velocity (Anschlagsgeschwindigkeit (Lautstärke der Note)) (wobei ich allerdings selber nicht weiß, was die Velocity bei NoteOff zu suchen hat) und bei Control Change bestimmt das 2. Byte, welcher Controller grade verändert werden soll und das 3. Byte den Wert, den der zu verändernde Controller kriegen soll. Eine Velocity von 0 (0x00) macht NoteOn übrigens automatisch zu NoteOff.
Man kann bei dem nächsten MIDI-Signal das Statusbyte übrigens auch weglassen und gleich mit dem 2. Byte anfangen, wenn die Signalart und der Kanal gleich bleiben.
Dann gibt es auch noch die so genannten SysEx-Nachrichten (System Exclusive), von denen ich vorhin auch gesprochen habe. Diese fangen immer mit 0xF0 an, haben dann eine beliebige Anzahl an Bytes, bei denen das 128er-Bit immer aus ist, und hören immer mit 0xF7 auf. Und da sie mit 0xF0 anfangen und mit 0xF7 aufhören, gehören sie keinem Kanal an. SysEx-Nachrichten haben nämlich gar nix mit der Musik zu tun, sondern sind zur so genannten Fernsteuerung von MIDI-Geräten da und kommen darum auch so gut wie nie in MIDI-Dateien vor. Wie diese Nachrichten für das jeweilige Gerät aufbebaut sein bzw. geschrieben werden müssen, steht immer in der Anleitung des Geräts drin, denn sie sind geräteabhängig (wenn das MIDI-Gerät diese Funktion auch unterstützt).

So, das war erstmal das Wichtigste von MIDI. Das sollte hierfür erstmal genügen.

Hängt das etwa damit zusammen, dass er dabei einen Arduino Leonardo statt einen Arduino Uno benutzt?

Durchaus möglich, dass das auch eine Rolle spielt.....

Denn beim Leonardo ist Serial1 frei.
Beim UNO ist Serial mit dem USB Konverter verbunden.

Hm... Aber er hat auch nur Serial statt Serial1 benutzt, und mit dem Sketch "Arduinoboy", das extra für den Arduino Uno gemacht wurde, funktioniert's ja auch.
Komisch!

Hi! Na, hat schon jemand von euch eine Idee oder im Arduinoboy-Sketch die Befehle zum empfangen gefunden?

PlayerX:
... hat schon jemand von euch eine Idee oder im Arduinoboy-Sketch ...

Mit einem Arduino MIDI zu empfangen und zu senden ist trivial.

Ich habe mir mal einen „universellen Zweier-Fader“, der MIDI sendet, gebaut. Hier ein wahllos aus meiner Sketch-Grabbelbox kopiertes Programm, daraus sollte alles hervorgehen, was Du brauchst:

// MIDI-Ding

// Wenn Ausgaben auf der seriellen Schnittstelle erfolgen sollen,
// Kommentar vor der folgenden Zeile entfernen:
#define DEBUG

// --- Ab hier konfigurierbare Optionen ---
const byte MIDI_CHANNEL_LEFT=1;
const byte MIDI_CHANNEL_RIGHT=1;
const byte MIDI_COMMAND_LEFT=0xb0;  // 0x80: Note Off
const byte MIDI_COMMAND_RIGHT=0xb0; // 0x90: Note On
                                    // 0xa0: Polyphonic Aftertouch
                                    // 0xb0: Controller Change
                                    // 0xc0: Program Change
                                    // 0xd0: Monophonic/Channel Aftertouch
                                    // 0xe0: Pitch Bending
                                    // 0xf0: System Message
const byte MIDI_CONTROLLER_NUMBER_LEFT=74;
const byte MIDI_CONTROLLER_NUMBER_RIGHT=11;
// --- Ende der konfigurierbaren Optionen ---

#include <SoftwareSerial.h>
#include <Piezo.h> // siehe http://html.szaktilla.de/weekender/3.html

Piezo audio(5);
void MIDISend(byte, byte, byte, byte);

struct Fader
{
  byte pin; // Analog-Pin an den der Fader angeschlossen ist
  byte channel; // Dem Fader zugeordneter MIDI-Kanal
  byte cmd; // Dem Fader zugeordneter Befehl
  byte controllerNumber; // Controllernummer
  byte lastValue; // Zuletzt geschickte Fader-Position
};

Fader leftFader;
Fader rightFader;

SoftwareSerial MIDIOut(2, 3); // MIDI-Out-Pins (RX, TX)
SoftwareSerial MIDIIn(4, 5); // MIDI-In-Pins (RX, TX)

const byte LEDPin=13;
byte incoming=0;
byte valueRight=0;
byte valueLeft=0;

void setup()
{
  leftFader.pin=A7;
  rightFader.pin=A6;
  
  leftFader.channel=MIDI_CHANNEL_LEFT;
  rightFader.channel=MIDI_CHANNEL_RIGHT;
  
  leftFader.cmd=MIDI_COMMAND_LEFT;
  rightFader.cmd=MIDI_COMMAND_RIGHT;
  
  leftFader.controllerNumber=MIDI_CONTROLLER_NUMBER_LEFT;
  rightFader.controllerNumber=MIDI_CONTROLLER_NUMBER_RIGHT;
  
  leftFader.lastValue=analogRead(leftFader.pin);
  rightFader.lastValue=analogRead(rightFader.pin);

#ifdef DEBUG
  Serial.begin(57600);
#endif  

  MIDIOut.begin(31250); 
  MIDIIn.begin(31250);
  pinMode(LEDPin, OUTPUT);
  digitalWrite(LEDPin, HIGH);

  audio.beep(READY);
}

void loop()
{
  if(MIDIIn.available() > 0)
  {
    do
      {
        incoming=MIDIIn.read();
        MIDIOut.write(incoming);
        #ifdef DEBUG
        Serial.print("In -> Out: ");
        Serial.println(incoming);
        #endif
      }
    while(MIDIIn.available() != 0);
  }

  valueLeft=analogRead(leftFader.pin)/8;
  if(valueLeft != leftFader.lastValue)
  {
    leftFader.lastValue=valueLeft;
    digitalWrite(LEDPin, LOW);
    MIDISend(leftFader.cmd, leftFader.channel, leftFader.controllerNumber, valueLeft);
    digitalWrite(LEDPin, HIGH);
    #ifdef DEBUG
    Serial.print  ("leftFader.lastValue=");
    Serial.println(leftFader.lastValue);
    #endif
  }
    
  valueRight=analogRead(rightFader.pin)/8;
  if(valueRight != rightFader.lastValue)
  {
    rightFader.lastValue=valueRight;
    digitalWrite(LEDPin, LOW);
    MIDISend(rightFader.cmd, rightFader.channel, rightFader.controllerNumber, valueRight);
    digitalWrite(LEDPin, HIGH);
    #ifdef DEBUG
    Serial.print  ("rightFader.lastValue=");
    Serial.println(rightFader.lastValue);
    #endif
  }
}

void MIDISend(byte cmd_, byte channel_, byte data1_ , byte data2_)
{
  MIDIOut.write(cmd_|channel_);
  MIDIOut.write(data1_);
  MIDIOut.write(data2_);

  #ifdef DEBUG
  Serial.print("sent: ");
  Serial.print(cmd_|channel_);
  Serial.print(",");
  Serial.print(data1_);
  Serial.print(",");
  Serial.println(data2_);
  #endif
}

Gruß

Gregor

Ach so? Dann wird MIDI also doch nicht über die Pins 0 und 1 gesendet und empfangen?
Und naja tut mir leid, aber leider kann ich hier nicht erkennen, an welcher Stelle genau ein MIDI-Signal empfangen wird. MIDI zu senden ist ja noch ganz einfach, aber das Empfangen funktioniert bei mir leider nicht.

Die Pins 0 und 1 dienen der Kommunikation mit USB/PC. da hängt die Transferhardware dran. Die sollte man vereinfacht gesagt nur in ganz spezuiellen Fällen und mur wenn man genau weiß, was man tut, für etwas anderes verwenden.

Gruß Tommy

Naja tut mir leid, aber das Shield benutzt leider Pin 0 und 1 und darum brauche ich genau diese beiden Pins. Den PC braucht man für mein Projekt später aber auch nicht mehr, warum das also auch kein Problem sein sollte.
Das Sketch für das Shield habe ich übrigens schon verlinkt (auch wenn der Pin für den MODE-Button ein anderer ist), damit ihr es euch anschauen könnt.

Vielen Dank, dass ihr euch die Zeit genommen habt, euch mal das Sketch für den Arduinoboy anzuschauen.
Jetzt habe ich herausgefunden, woran es lag, dass mein Arduino Uno die MIDI-Signale nicht richtig erkannt hatte.
Die fehlenden Befehle liegen gleich im 1. Tab des Sketches (Arduinoboy.ino).

[...]

int pinMidiInputPower = 4; // power pin for midi input opto-isolator

[...]

pinMode(pinMidiInputPower,OUTPUT);
digitalWrite(pinMidiInputPower,HIGH); // turn on the optoisolator

[...]

Und zwar muss der Opto-Isolator auch noch eingeschaltet werden, da er an einem digitalen Pin hängt statt am +5V-Pin. Und das soll man nun wissen.

Und zwar muss der Opto-Isolator auch noch eingeschaltet werden, da er an einem digitalen Pin hängt

?

Keine Ahnung, ob das hier überhaupt passt, aber generell hat ein Optokoppler auf der Ausgangsseite nur zwei Pins, die man an Sekundär-GND und einem INPUT_PULLUP-Pin anschließen kann.

Nennt man auch OpenCollector - Ausgang.