[erledigt] Grenzen von SoftwareSerial erreicht?

Hallo allerseits!

Angeregt durch MIDI-Gebastel für jemand anderen habe ich mir ebenfalls ein „MIDI-Ding“ gebaut: Ein Arduino mit Schiebepoti, der die Position des Schiebepotis per SoftwareSerial als MIDI-Befehl auf der MIDI-out-Buchse ausgibt. Es gibt außerdem eine Buchse für eingehende MIDI-Nachrichten. Was dort eintrifft, soll unverändert an MIDI-out durchgereicht werden.

Problem ist, dass Daten, die auf MIDI-in eintreffen, nicht zuverlässig und/oder verzögert auf MIDI-out ausgegeben werden. Für Leute, die auf Determinismus stehen, eine Horrorvorstellung.

Der Sketch:

// MIDI-Ding

#include <SoftwareSerial.h>
#include <Piezo.h>

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

// --- Ab hier konfigurierbare Optionen ---
const byte MIDI_CHANNEL=1;
const byte MIDI_COMMAND=0x90;  // 0x80: Note Off
                               // 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=74;
// --- Ende der konfigurierbaren Optionen ---

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 fader;

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 value=0;

void setup()
{
  fader.pin=A7;
  fader.channel=MIDI_CHANNEL;
  fader.cmd=MIDI_COMMAND;
  fader.controllerNumber=MIDI_CONTROLLER_NUMBER;
  fader.lastValue=analogRead(fader.pin);

  #ifdef DEBUG
  Serial.begin(9600);
  #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);
  }

  value=analogRead(fader.pin)/8;
  if(value != fader.lastValue)
  {
    fader.lastValue=value;
    #ifndef DEBUG
    digitalWrite(LEDPin, LOW);
    #endif
    MIDISend(fader.cmd, fader.channel, value+50, 127);
    #ifndef DEBUG
    digitalWrite(LEDPin, HIGH);
    #endif
    #ifdef DEBUG
    Serial.print  ("fader.lastValue=");
    Serial.println(fader.lastValue);
    #endif
  }
}

void MIDISend(byte cmd_, byte channel_, byte data1_ , byte data2_)
{
  #ifdef DEBUG
  digitalWrite(LEDPin, LOW);
  #endif

  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_);

  digitalWrite(LEDPin, HIGH);
  #endif
}

Wie kann ich der Ursache des Problems auf die Schliche kommen und wie kann ich sie abstellen, wenn überhaupt?

Vielen dank vorweg!

Gregor

PS: Piezo.h ist eine eigene Bibliothek. Siehe hier.

Kurz und schnell beantwortet “JA”.
Die Library ist nicht dafür geeignet, gleichzeitig 2 Schnittstellen zu bedienen.
Also du kannst nicht gleichzeitig empfangen und senden.
Nimm für einen Weg die Hardware-Serielle.

HotSystems:
Also du kannst nicht gleichzeitig empfangen und senden.

Dann kann ich das Fluchen ja einstellen. Danke!

Gregor

gregorss:
Dann kann ich das Fluchen ja einstellen. Danke!

Gerne…das ist doch mal eine gute Entscheidung. :wink:

Mit einem MEGA sollte es klappen sie haben 4 Hardware Serial. Evtl wäre sogar ein MEGA2560 Core gut zu nutzen der ist nämlich extrem kompakt.
Gruß
DerDani

Naja, ein Atmega328 als Standalone tut es auch. Da kann als 2. Serielle die UART verwendet werden.

Eigentlich brauchst Du doch nur jeweils eine halbe Serielle. Incomming RX ound Outgoing TX. Das sollte doch auch eine Softserial schaffen oder täusche ich mich da?

Gruß Tommy

Tommy56:
Eigentlich brauchst Du doch nur jeweils eine halbe Serielle. Incomming RX ound Outgoing TX. ...

DAS isn Argument!

Gruß

Gregor

Tommy56:
Eigentlich brauchst Du doch nur jeweils eine halbe Serielle. Incomming RX ound Outgoing TX. Das sollte doch auch eine Softserial schaffen oder täusche ich mich da?

Ich befürchte, das kommt auf selbe raus. Gleichzeitig empfangen und senden geht nicht.

Ok, also auch auf einer Softserial nicht. Das war meine implizite Unklarheit. Dann ist die ja eigentlich kaum für irgendwas Sinnvolles zu gebrauchen, ausser man braucht definitiv nur 1 Richtung.

Gruß Tommy

Tommy56:
Ok, also auch auf einer Softserial nicht. Das war meine implizite Unklarheit. Dann ist die ja eigentlich kaum für irgendwas Sinnvolles zu gebrauchen, ausser man braucht definitiv nur 1 Richtung.

Gruß Tommy

Naja, stimmt nicht ganz.
Ich verwende die in einigen Projekten.
Aktuell für mein Nextion und einem DFPlayer Mini in meinem Radiowecker.
Das läuft super, nur eben nicht gleichzeitig.

Wie stellst Du beim Nextion sicher, dass nicht gleichzeitig z.B. der Arduino etwas zum Nextion sendet und dort jemand einen Button betätigt, den das Nextion an den Arduino senden will?

Dann dürfte das doch schief gehen.

Gruß Tommy

Tommy56:
Wie stellst Du beim Nextion sicher, dass nicht gleichzeitig z.B. der Arduino etwas zum Nextion sendet und dort jemand einen Button betätigt, den das Nextion an den Arduino senden will?

Dann dürfte das doch schief gehen

Stimmt, das war anfangs auch ein kleines Problem.
Ich aktualisieren den Nextion nur alle Sekunden und wenn ich Einstellungen vornehme, wird der Datenfluß vom Arduino angehalten. Ausser bei den Einstellungen, die werden sofort angezeigt.
Zusätzlich steuere ich noch einige Funktionen per IR mit meinem ATtiny85-Modul und IRQ.
Das funktioniert soweit sehr gut.

Wenn ich Euch richtig verstehe, ist weitere Hardwarebastelei die einzige Lösung. Oder nicht?

Irgendwo habe ich noch so ein Bild von der Beschaltung der MIDI-in/-out/-thru-Buchsen ...

Gruß

Gregor

gregorss:
Wenn ich Euch richtig verstehe, ist weitere Hardwarebastelei die einzige Lösung. Oder nicht?

Irgendwo habe ich noch so ein Bild von der Beschaltung der MIDI-in/-out/-thru-Buchsen ...

Da ich keine Ahnung von MIDI habe, kann ich nur wage Tipps geben.

Ich würde es tatsächlich mit einem Atmega328-Solo und der Hardware-UART sowie einer SoftwareSerial machen.

HotSystems:
Da ich keine Ahnung von MIDI habe, kann ich nur wage Tipps geben.

MIDI ist serielle Hausmannskost, bis auf die Baudrate.

HotSystems:
Ich würde es tatsächlich mit einem Atmega328-Solo und der Hardware-UART sowie einer SoftwareSerial machen.

Ich werde mir auf jeden Fall mal ansehen, was es mit der zweiten ser. Schnittstelle des ATmega328 auf sich hat. Evtl. spiele ich damit weiter.

Gruß

Gregor

gregorss:
MIDI ist serielle Hausmannskost, bis auf die Baudrate.

Ich werde mir auf jeden Fall mal ansehen, was es mit der zweiten ser. Schnittstelle des ATmega328 auf sich hat. Evtl. spiele ich damit weiter.

Ja, Versuch macht Kluch…

Da Midi doch unidirektional ist, reicht doch ein vorhandener hw-uart aus, wenn man kein Debugging mehr braucht. Oder habe ich da was falsch verstanden?

gregorss:
Problem ist, dass Daten, die auf MIDI-in eintreffen, nicht zuverlässig und/oder verzögert auf MIDI-out ausgegeben werden. Für Leute, die auf Determinismus stehen, eine Horrorvorstellung.

Da wäre doch eine Hardware Verküpfung von Midi-In oder Arduino Midi-Out nach Midi-Out mit einem 74LS00 am schnellsten und am sichersten.

Alternativ mit Interupts arbeiten. Auf jeden Fall das Mischen der beide Signale nicht in Midi-Logik, sondern auf reine binärebene realisieren.

Ich habe das gerade mal ausgemessen, das reine Weitersenden eines Bytes sind zwischen 15-20 uS. Das dürften auch Determinismus-Anhänger beruhigen :slight_smile:

Da muss man dann halt die Potentiometereinstellung zwischen die 3-Byte Telegramme schieben.

void setup() {
  Serial.begin(38400);
}

void loop() {
  // put your main code here, to run repeatedly:
  uint32_t zeit = micros();

  if (Serial.available()) {
    Serial.print((char)Serial.read());
    zeit = micros() - zeit;

    Serial.print("  Zeit:\t");
    Serial.println(zeit);


  }

}}