Midi Out am NANO Programmieren

Hallo zusammen,
habe ein Problem mit Midi Senden. Wenn ich in den Code Prints
rein schreibe Funktioniert es einiger maßen ohne die Prints
meldet mein Soundexpander, die Daten würden schneller kommen als er sie verarbeiten kann.

Was kann das sein?

Ich hänge hier mal den Code aus dem Sketch rein->

#include <Arduino.h>
#include <EEPROM.h>
#include <LiquidCrystal_I2C.h> // Vorher hinzugefügte LiquidCrystal_I2C Bibliothek einbinden

LiquidCrystal_I2C lcd(0x27,40,2); // 2 zeiliges I2C-LCD mit 40 Zeichen pro Zeile ...

#include "defauls.h"
#include "proto_.h"
#include "atachment_.h"
#include "progs_.h"

void setup()
{

        lcd.init(); //Im Setup wird der LCD gestartet 
        lcd.backlight(); //Hintergrundbeleuchtung einschalten 
        lcd.leftToRight(); 

        lcd.clear();
        lcd.home();

        pinMode(pAD0_,OUTPUT);
        pinMode(pAD1_,OUTPUT);
        pinMode(pAD2_,OUTPUT);
        pinMode(pAD3_,OUTPUT);
        pinMode(13,OUTPUT);

        pinMode(pD10_,OUTPUT);
        pinMode(pD11_,OUTPUT);
        pinMode(pD12_,OUTPUT);

        pinMode(pD02_,INPUT);
        pinMode(pD03_,INPUT);
        pinMode(pD08_,INPUT);

        pinMode(MidiOut_M1_,OUTPUT);
        pinMode(MidiOut_M2_,OUTPUT);

        digitalWrite(MidiOut_M1_,HIGH); // midiout 1+2 an ...
        digitalWrite(MidiOut_M2_,HIGH);
  
        Serial.begin(31250); // midi sequenz ....

        lcd.setCursor(dspl_v_,line_1_);// das erste Zeichen in der ersten Zeile. 
        lcd.print(" Midi Test "); 

        zTimer_1 = 0;

        attachInterrupt(0,INT_0_,LOW); // poti top und tasten ...
        attachInterrupt(1,INT_1_,RISING); // nur poti links / rechts ...

}

void loop()
{

//******************* midi in abfrage ... ******************************

        iBytes_ = Serial.available();
        if (iBytes_ > 0) 
        {
        detachInterrupt(0);
        detachInterrupt(1);

        lcd.setCursor(dspl_v_,line_2_);// das erste Zeichen in der zweiten Zeile. 

        while (iBytes_-- > 0)    
        {
        
        //Serial.write(Serial.read());
        
        bOUT_ = Serial.read();
        lcd.print(" "); 
        lcd.print(bOUT_); 
        Serial.write(bOUT_);
      
        }
        lcd.print(" "); 
    
        digitalWrite(13, !(digitalRead(13))); // Pin togglen

        attachInterrupt(0,INT_0_,LOW); // nur poti links / rechts ...
        attachInterrupt(1,INT_1_,RISING); // poti top und tasten ...

        }

//**************** timer ***********************************************

          if (millis() > zTIMER_) // abfrager begrenzer ...
          {

          zTIMER_ = millis()+100;

          if (zTIMER_ > zUSER_TIME_)
                 fFlipFlop_ = ON;

            zPop_=1;
    
      } // millis ...
}// loop

Im englischen Teil des Forum müssen die Beiträge und Diskussionen in englischer Sprache verfasst werden. Deswegen wurde diese Diskussion in den deutschen Teil des Forums verschoben.

mfg ein Moderator.

Hallo,
im deutschen Teil konnte ich mich nicht anmelden.

ich kann mich mit meinem Benutzer Namen nicht anmelden, obwohl mein
PW korrekt eingetippt wurde.

Ich habe mein PW vergessen<< geht auch nicht, weil die Seite meine neue email als falsch ansieht.

Wenn ich nach meinem Benutzer Namen BenTuck suche,
findet er meine alten Eintragungen.
Wollte eine neue Frage stellen, komme nicht hinein ins Forum.

Bitte mal prüfen was da los ist.
lg Roland

Warum verwendest du nicht https://docs.arduino.cc/built-in-examples/communication/Midi/ statt

Was ist das?

Du gehst davon aus, dass alle Zeichen printable sind. Das könnte Dir das Genick brechen, wenn Du das Teil als Sniffer benutzen willst.

Die 1.te Serielle dafür zu benutzen halte ich für unglücklich. USB und Serieller Monitor ist fast unverzichtbar.

Sieht fast aus, als soll das auf einem UNO/NANO laufen.

Ersetze (13) durch (LED_BUILTIN), dann ist das ggfls. auch auf andere Controller übertragbar.

In den Dateien sind angelegte Variablen usw. die haben mit der Midi Funktion selber nichts zutun.

Die Ausgabe auch dem Display habe ich nur gemacht, weil ich mir nicht erklären konnte warum es diese Fehlermeldung
auf meinem Expander gibt und die Baudrate ja Midi mäßig eingestellt ist.
Die Prints bringen nur eine Verzögerung zwischen den Sendebytes und damit ist mein Expander zufrieden. Aber das kann ja keine Lösung sein.

Die Bytes werden einwandfrei auf dem Display ausgegeben. Aber das benötige ich nicht. Ist nur für den Test kann auch wegbleiben. Aber dann meldet mein Soundexpander die Daten würden zu schnell kommen. Ja es ist ein NANO.

Welchen technischen Grund gibt es dafür?

Habe eine Schaltung mit der die Schnittstelle vom Midi getrennt ist wenn der NANO programmiert wird.

Ich vermute mal, dass da ein TimeOut beim Expander zuschlägt, wenn Du zwischendurch etwas anderes machst und der dann dazu führt dass wieder von vorn gelesen wird.
Grund könnte sein, dass ein Zeichen fehlt...

#include <SoftwareSerial.h>

SoftwareSerial midi (2, 3);
uint8_t x;
void setup()
{
  Serial.begin(115200); // Seriallen Monitor auch so einstellen!
  midi.begin(31250);
  Serial.println(F("Start..."));
}

void loop()
{
  while (midi.available())
  {
    Serial.print(midi.read(), HEX);
    Serial.print(", ");
    x++;

    if (x >= 9)
    {
      Serial.println();
      x = 0;
    }
  }
  Serial.flush();
}

Verbinde Deinen SENDER-OUT mit PIN 2 vom NANO.
Den TX lässt Du frei.

Dann steckst Du das USB-Kabel an und überträgst den Sketch.
Dann machst Du den Seriellen Monitor auf und stellst den (unten rechts) auf 115200.

Wenn Du jetzt was von Deinem Sender ausgibst, sollte das entsprechend auf dem SerMon zu sehen sein.

Ich will die Ausgabe mal sehen.
(Bitte kopieren und kein Screenshot)

Eine 1000end-fach erprobte Lösung einsetzen?
Viel einfacherer Code?

Moin @bentuck ,

wenn man mal alles "Geheimnisvolle", die LCD-Anteile und weiteres Beiwerk aus Deinem Sketch entfernt, bleibt das hier (rein funktional, ohne Berücksichtigung der Variablendeklaration) übrig:

void setup()
{
  Serial.begin(31250); // midi sequenz ....
}

void loop(){
  iBytes_ = Serial.available();
  if (iBytes_ > 0) {
    while (iBytes_-- > 0) {
      bOUT_ = Serial.read();
      Serial.write(bOUT_);
    }
  }
}// loop

Demnach würden die Bytes, die über die serielle Schnittstelle auf RX einlaufen, auf TX wieder gesendet werden ... ?!?

Die weiteren Funktionen des Sketches entziehen sich unserer Kenntnis.

Welche Funktion haben z.B. die Pins MidiOut_M1_ und ..M2

  pinMode(MidiOut_M1_, OUTPUT);
  pinMode(MidiOut_M2_, OUTPUT);

Was verbirgt sich hinter den Interrupt-Routinen INT_0_ und INT_1_

 attachInterrupt(0,INT_0_,LOW); // nur poti links / rechts ...
 attachInterrupt(1,INT_1_,RISING); // poti top und tasten ...

die den Interrupts 0 und 1 zugeordnet sind, aber mit dem Eintreffen von Bytes detached und nach Abschluss der Weiterleitung wieder attached werden?

Wenn diese Anteile tatsächlich irrelevant sind, dann wäre Dein Sketch bzgl. des Expanders ein reiner MIDI-Repeater und es müsste an der Quelle der MIDI-Daten liegen.

Nur ein Nebenaspekt: Die zeitabhängige if-Abfrage

  if (millis() > zTIMER_) // abfrager begrenzer ...
  {

    zTIMER_ = millis() + 100;

    if (zTIMER_ > zUSER_TIME_)
      fFlipFlop_ = ON;

    zPop_ = 1;

  } // millis ...
      

entspricht nicht dem als überlaufsicher empfohlenen Vorgehen. Vermutlich wird Deine Anwendung selten oder nie solange laufen, dass der Überlauf des millis()-Zählers zu erwarten ist. Aus grundsätzlichen Erwägungen (der Mensch ist ein Gewohnheitstier und behält "schlechte" Angewohnheiten auch gern bei, wo es schädlich ist :wink: ) sollte man millis() nicht gegen eine Vorgabezeit prüfen, sondern stets die Differenz zum letztem Durchgang prüfen. Das könnte in Deinem Sketch z.B. so aussehen (allerdings ohne zu wissen, wie zUSER_TIME_ zustand kommt):

  //if (zTIMER_-millis() > 100) // abfrager begrenzer ...
  //Korrigiert, siehe Post 13, Danke MicroBahner!!
  if (millis() - zTIMER_> 100) // abfrager begrenzer ...
  {

    zTIMER_ = millis();

    if (zTIMER_+100- zUSER_TIME_ > 0){
      fFlipFlop_ = ON;
    }
    zPop_ = 1;

  } // millis ...

Letzteres hilft Dir sicher nicht bei Deinem Problem ...

Auf jeden Fall viel Erfolg!
ec2021

Sollte
if (millis() - zTIMER_ > 100) // abfrager begrenzer ...
sein. Sonst wird das nie wahr :wink: .

War in meinen Gedanken wohl schon woanders ... :wink:
Danke!
ec2021

Hi Hallo Faraday,
der Fehler lag bei mir. Die Funktion iBytes_ = Serial.available(); ist so nicht richtig angewendet.

Ich habe es jetzt so gemacht.
Zunächst muss ich ja wissen, ob über Midi überhaupt etwas herein kommt. Weil dann ja die Interrupts ausgeschaltet werden müssen damit mir die nicht dazwischen funken. Dann sollen die Meldungen Zeitnah verarbeitet werden.

Hier nun der korrigierte Code und der Funktioniert. Die Prints werden nicht benötigt als Zeitpuffer.

void loop()
{
    if (Serial.available()) 
{
         // hier interrupts aus !

while (Serial.available())
 {
      bOUT_ = Serial.read(); 
      Serial.write(bOUT_);
    }

 // hier Interrupts wieder an!

  } // Serial.available()
}// loop

Bei dem ganzen Programm handelt es sich um eine Möglichkeit meine beiden Soundexpander gleichzeitig oder einzeln einzuschalten. Das mache ich mit einem 74HC4316.
Danach werden die 2 OUT Signale über ein 74HC04 an Midi Out geschickt. Genau wie normale Midi-Schaltung aufgebaut ist.
Mit den Digi Potis kann ich die Sounds einstellen usw.
Die Einstellungen werden dann im Eeprom des Nano gespeichert. Es sind jeweils 72 Bytes für eine Sound Zusammenstellung. Viel mehr als 5 Programme benötige ich nicht.
Die Expander steuere ich dann mit meinem Tastencontroler an.

Habe das jahrelang mit einem AT89C52ED2 gemacht. Aber die Programme zum Flashen usw. sind alle nur 16BIt und die laufen nicht so einfach auf dem WIN11/64 System. Außerdem habe ich vor ein paar Jahren mit Arduino angefangen und bin so begeistert wie Easy das alles ist und werde da bei bleiben. Im Nachhinein hätte ich schon 10 Jahre früher umsteigen sollen. Das hätte mir viel Arbeit erspart.
Man bekommt ja verschiedene Größen fertig aufgebaut und kann sie direkt wie ein IC auf sein eigenes Projekt aufstecken.

Auch Serial nutzt Interrupts, weshalb Dir einzelne Bytes verloren gehen könnten.

Dann halte ich die gleichzeitige Nutzung von Serial für MIDI und neue Programme für problematisch, da Dir der serielle Monitor fehlt.

Besser wäre der Mega2560 mit gleich drei zusätzlichen UARTs wie im erfolgreich abgeschlossenen Thema MIDI Controller mit 92 Möglichkeiten bauen oder einen preiswerteren ESP32 mit zwei freien UARTs. Der ESP32 hat anstelle des EEPROMs ein eigenes Dateisystem für Voreinstellungen und kann mit der Arduino-IDE programmiert werden.

Nur so als Anregung :blush:

Ganz klar ist mir Deine Anwendung noch nicht, insofern kann ich nicht beurteilen, ob Deine Probleme damit beseitigt sind.

Der Pin 2 oder 3 am Nano ist also irgendwie mit Serial-RX verbunden? Dann würden beim Empfang von Daten Interrupts ausgelöst werden. Unter Umständen aber mehr, als für die Anwendung gewünscht.

Bei einer Baudrate von 31250 Bd:

  • ergibt RISING ca. 2 bis 4 ISR Aufrufe pro empfangenem Byte
  • und LOW bis zu 40 Aufrufe pro empfangenem Byte
  • 0 Aufrufe jeweils erst ab dem sechsten Character im Buffer

Das passiert so trotz des detach-Aufrufs, wie das Wokwi-Beispiel zeigt:

/*
   Forum: https://forum.arduino.cc/t/midi-out-am-nano-programmieren/1394108
   Wokwi: https://wokwi.com/projects/435923199268581377

   Interrupt-Aufrufe bei seriellem Dateneingang

   Durch Ändern des Wertes von "mode" lassen sich die unterschiedlichen Aufrufhäufigkeiten
   testen.

   RISING -> 1 bis 4 Aufrufe pro Byte
   LOW    -> ca. 11 bis 40 Aufrufe pro Byte 

   (0 Aufrufe jeweils erst ab dem sechsten Character im Buffer)

   2025/07/09
   ec2021

*/


constexpr byte isrPin {3};
constexpr byte mode {LOW};  // Can be set to LOW, CHANGE, RISING, FALLING
volatile int count = 0;

void setup() {
  Serial.begin(31250);
  Serial.println();
  pinMode(isrPin, INPUT_PULLUP);
  attach();
}

void loop() {
  if (Serial.available()) {
    detach();
    while (Serial.available()) {
      char c = Serial.read();
      switch (c) {
        case '\n':
          Serial.print("[NL]");  // New Line = "Zeilenvorschub"
          break;
        case '\r':
          Serial.print("[CR]");  // Carriage Return = "Wagenrücklauf"
          break;
        default:
          Serial.print(c);
      }

    }
    printCount();
    attach();
  }
}

void ISRoutine() {
  count++;
}

void printCount() {
  noInterrupts();
  int actCount = count;
  count = 0;
  interrupts();
  if (actCount > 0) {
    Serial.print("\t Count =\t");
    Serial.println(actCount);
  }
}

void detach() {
  detachInterrupt((digitalPinToInterrupt(isrPin)));
}


void attach() {
  attachInterrupt((digitalPinToInterrupt(isrPin)), ISRoutine, mode);
}



Zum Ausprobieren Test ISR Aufrufe

Habe den Sketch auf einen Nano geflasht und bei LOW und RISING vergleichbare Werte erhalten. Insbesondere beim Triggern auf LOW ergaben sich für drei Byte um die 60 Interruptaufrufe.

Ob das ok für Dich ist, weiß ich natürlich nicht.

Möglicherweise ließe sich Deine Anwendung verträglicher (ohne Interruptroutinen) lösen. Dazu müsste man aber die fehlenden Anteile kennen, oder besser sogar, eine Beschreibung der von Dir verwendeten Hardware, der aktuellen Verdrahtung und des gewünschten Zusammenwirkens...

Das liegt natürlich bei Dir!

Gruß
ec2021

Hi - ich habe für alles Einzel-Geräte angeschafft. Die Kurbelei wurde mir zu mühsam, deshalb habe ich mir einen speziellen Controler gebaut, wo alles ein bischen zusammen gefasst ist.
Hier ist ein Link für ein Bild davon->
Teil 1 um was geht es

Hi - die Interrupts sind nur für die Taster und die Digipotis. Ein Interrupt soll nur die Tasten übernehmen und der andere die Links / Rechts Drehung. Das funzt einwandfrei.
Die Rx und Tx Pins sind nicht an diesen Int0 und Int1 Interrupts dran. Jedenfalls habe ich da nichts eingestellt.
Die Midi Geschichte funktioniert auch einwandfrei.
Habe hier was gepostet->Post #15
Mein Spezial-Controler ist zwischen meine beiden Soundexpander und dem Tastencontroler geschaltet. Deshalb
müssen die Mididaten gleich wieder hinaus geschickt werden. Manche davon filtere ich heraus um z.B. eine Ausgabe auf das Display zu veranlassen.
Das Prog hatte ich vor ca. 15 Jahren geschrieben und es lief die ganzen Jahre auf einem AT89C52ED2. Dann bin ich vor ein paar Jahren auf Arduino umgestiegen und fertige seit dem jedes Projekt damit an.
Ich musste etwas an dem Prog ändern und bemerkte dabei was für Umstände damit zusammen hängen. Jetzt hatte ich mich entschieden die Platine für die Steuerung und die für die
Potis neu zumachen. Das war eine gute Entscheidung. Noch einige Verbesserrungen mit eingebaut und jetzt bin ich dabei das Prog für Arduino zuschreiben. Die unnötigen Sachen des alten Programms lasse ich dabei weg.
Bin begeistert wie esay das mit Arduino geht.

Jetzt weiß ich nicht was am HC138 hängt.
Der 138 ist kein Port expander sondern ein Multiplexer. Nur der eingestellte "Ausgang" hat ein HIGH bzw LOW Signal. Alle anderen sind Hochohmig.

Das kann zu Problemen führen, wenn die angeschlossenen Eingänge Probleme mit nicht definierten Logikpotentialen hat. Gleiches Problem mit Tastern am Arduino ohne Pullup/Pulldown Widerstand.

Da nimmst Du besser ein Schieberegister (zB hc595) oder einen I2C bzw SPI Port Expander.

Grüße Uwe

[EDIT]
Der HC138 ist ein Decoder. Je nach Eingangssignal (3Bit) ist einer der 8 Ausgänge LOW. Die anderen sind HIGH.
Das bedingt daß immer nur ein Ausgang verwendet werden kann.
Mit einem Schieberegister oder I2C/SPi Expander bist Du da frei in der Wahl, welche und wieviele Ausgänge HIGH oder LOW sein sollen.
[/EDIT]