MIDI Controller PART 3

nein.
Du musst ja den onboard TTL-USB Wandler reusen. Also musst du am B jeweils mit TX0 bzw. RX0 verbinden!!!

ebenso würde es Sinn machen, am A immer den TX1/RX1 oder den TX2/RX2 zu verwenden. Je nach dem was du am A tatsächlich nutzt für deine Ausgaben die (über B) dann am PC ankommen soll!

1 Like

Wenn TX1 A an RX0 B geht, bleibt TX2 A in der Luft.
Es gibt 2 MIDI-Ausgänge.

1 Like

O.k.
Da warte ich dann noch ab, bis ich eine eindeutige Anleitung habe, klingt jedenfalls nach schnell gemacht.

...offen ist ja auch noch die Änderung bei den LED´s der Gruppe zu MODE C

Stand jetzt nur Mode A und B

//          A = 1 von Gruppe   B= alle von Gruppe
Gruppe grp[] {
	{99, 99, 'A'},  // 0: ohne Gruppe
	{ 0, 13, 'A'},  // 1: Gruppe O
	{14, 21, 'B'},  // 2: Gruppe N
	{24, 35, 'A'},  // 3: Gruppe Chaser
	{36, 41, 'A'},  // 4: Gruppe POSITIONS
	{42, 47, 'B'},  // 5: Gruppe M
};

Mode C wäre dann so, das O und N zusammen gefasst werden:

	{ 0, 13, 'C'},  // 1: Gruppe O
	{14, 21, 'C'},  // 2: Gruppe N

Auch da bin ich neugierig, weil fürs füllen der [] ist dann ja eine Gruppe zu wenig
hmmmm...
wird dann an anderer Stelle O und N = auf diese Werte gesetzt? So stelle ich es mir jedenfalls vor

   { 0, 21, 'C'},   // 1: Gruppe O
   {99, 99, 'C'},  // 2: Gruppe N

ich bin dann auch den Rest des Tages verplant. Einen schönen Samstag euch allen.

LG Rebecca

1 Like

Fertig.

Code für den Debug-MEGA:

// Erster Ansatz
// Das mit dem doppelten Code ist Absicht!
// Bitte beachten, dass es derzeit möglich ist, dass zuerst CHannel B und dann Channel A auf HM ausgegeben wird
/*
   Serial  : (TX0/RX0) -> USB Port ->> HairlessMidi
   Serial1 : RX1 an TX1 des Sender -> MIDI Channel A
   Serial2 : RX2 an TX2 des Sender -> MIDI Channel B
   GND     : GND
*/

constexpr uint32_t noNoteTimeout {5};   // Zeit in ms
uint32_t readTime;

void setup()
{
  Serial.begin(38400);    // HairlessMidi
  Serial1.begin(31250);   // Receive Channel A
  Serial2.begin(31250);   // Receive Channel B
}

void loop()
{
  if (Serial1.available())                         // check ob was da ist
  {
    readTime = millis();                           // Erste Zeit merken

    while (millis() - readTime < noNoteTimeout)    // Mache solange, bis das Timeout zuschlägt
    {
      int receive = Serial1.read();                // einlesen

      if (receive >= 0)                            // nutzbares Byte? (nichts zu lesen gibt -1 aus)
      {
        Serial.write(receive);                     // ausgeben
        readTime = millis();                       // Zeit merken für neues Timeout
      }
    }
  }

  if (Serial2.available())                         // check ob was da ist
  {
    readTime = millis();                           // Erste Zeit merken

    while (millis() - readTime < noNoteTimeout)    // Mache solange, bis das Timeout zuschlägt
    {
      int receive = Serial2.read();                // einlesen

      if (receive >= 0)                            // nutzbares Byte? (nichts zu lesen gibt -1 aus)
      {
        Serial.write(receive);                     // ausgeben
        readTime = millis();                       // Zeit merken für neues Timeout
      }
    }
  }
}

Code für den King:

/* Belegung Mega2560
        CIPO = 50   unbenutzt (früher MISO)
  DATA  = COPI = 51   Pin von Hardware-SPI (früher MOSI)
  CLOCK = SCA  = 52   Pin von Hardware-SPI
  LATCH = CS   = 53   Chip Select Pin
  OE           = 49   Output Enable, frei wählbarer Pin
*/
/* Usecase by noiasca 2025-11-16 3870/218 in tlc.h */
// #define agmue

#include <Wire.h>
// EXPANDER (PCF8575)----------------------------------------------------------------------------------------------
constexpr uint8_t inputPins {16};            // Anzahl Pins am PCF8575
constexpr uint8_t kOff {143};                // Kanaloffset
constexpr uint32_t pcfMessIntervall {5000};  // in µs
uint32_t pcfVorhin;
uint16_t read16(uint8_t adresse);

#include "gruppen.h"
#include "tlc.h"

class PCF_Board {
    const uint8_t i2cAdd;
    const uint8_t pcfNum;
    uint16_t wertAlt = 0;
  public:
    PCF_Board(const uint8_t i2cAdd, const uint8_t pcfNum) : i2cAdd(i2cAdd), pcfNum(pcfNum) {};

    void init() {
      wertAlt = read16(i2cAdd);
    }
    //
    void auswertung() {
      uint16_t wert = read16(i2cAdd);
      uint16_t diff = wertAlt ^ wert;
      wertAlt = wert;

      if (diff) {
        for (byte j = 0; j < inputPins; j++) {
          if (bitRead(diff, j)) {
            byte idx = j + pcfNum * inputPins;

            if (bitRead(wert, j)) {
              // SerMon
              Serial.print(idx); Serial.print('\t'); Serial.print(taste[idx].led); Serial.print('\t'); Serial.print(kOff + taste[idx].midiAKanal); Serial.print('\t'); Serial.print(taste[idx].midiANote); Serial.print("\t127\t"); Serial.print(kOff + taste[idx].midiBKanal); Serial.print('\t'); Serial.print(taste[idx].midiBNote); Serial.println("\t127");
              Serial.print(idx); Serial.print('\t'); Serial.print(taste[idx].grpIDX); Serial.print('\t'); Serial.print(grp[taste[idx].grpIDX].mode); Serial.print('\n');
              // MidiChannel A
              Serial1.write(kOff + taste[idx].midiAKanal); Serial1.write(taste[idx].midiANote); Serial1.write(127);
              // Midi Channel B
              Serial2.write(kOff + taste[idx].midiBKanal); Serial2.write(taste[idx].midiBNote); Serial2.write(127);

              if (grp[taste[idx].grpIDX].mode != 'B') {
                Serial.print(idx); Serial.print('\t'); Serial.print(grp[taste[idx].grpIDX].ersteLED); Serial.print('\t'); Serial.print(grp[taste[idx].grpIDX].letzteLED); Serial.print('\n');

                for (uint8_t j = grp[taste[idx].grpIDX].ersteLED; j <= grp[taste[idx].grpIDX].letzteLED; j++) {
                  tlc.off(j);
                }
              }

              tlc.toggle(taste[idx].led);
            }
            else {
              // SerMOn
              Serial.print(idx); Serial.print('\t'); Serial.print(taste[idx].led); Serial.print('\t'); Serial.print(kOff + taste[idx].midiAKanal); Serial.print('\t'); Serial.print(taste[idx].midiANote); Serial.print("\t0\t"); Serial.print(kOff + taste[idx].midiBKanal); Serial.print('\t'); Serial.print(taste[idx].midiBNote); Serial.println("\t0");
              // Midi Channel A
              Serial1.write(kOff + taste[idx].midiAKanal); Serial1.write(taste[idx].midiANote); Serial1.write(0);
              // Midi Channel B
              Serial2.write(kOff + taste[idx].midiBKanal); Serial2.write(taste[idx].midiBNote); Serial2.write(0);
            }
          }
        }

        tlc.update();
      }
    }
};
#ifdef agmue
PCF_Board pcfBoard[] { { 0x3A, 0 }, { 0x3B, 1 }, { 0x3C, 2 }, { 0x38, 3 }, { 0x20, 4 }, { 0x21, 5 } };
#else
PCF_Board pcfBoard[] { { 0x20, 0 }, { 0x21, 1 }, { 0x22, 2 }, { 0x23, 3 }, { 0x24, 4 }, { 0x25, 5 } };
#endif
//
void loop()
{
  uint32_t jetzt = micros();

  if (jetzt - pcfVorhin >= pcfMessIntervall)
  {
    pcfVorhin = jetzt;

    for (PCF_Board &p : pcfBoard)
    {
      p.auswertung();
    }
  }
}
//
void setup()
{
  Serial.begin(115200);  // Texte und Upload
  Serial.println("\nStart ...");
  Serial1.begin(31250);  // MIDI HairlessMIDI mit 38400 Standard MIDI mit 31250
  Serial2.begin(31250);  // MIDI HairlessMIDI mit 38400 Standard MIDI mit 31250
  delay(1000);
  Wire.begin();
  Wire.setClock(400000);

  for (PCF_Board &p : pcfBoard)
  {
    p.init();
  }

  SPI.begin();
  tlc.start();
  tlc.update();
  pinMode(pinOE, OUTPUT);
  digitalWrite(pinOE, LOW);
}
//
uint16_t read16(uint8_t adresse)
{
  const uint8_t anzahl = 2;
  Wire.requestFrom(adresse, anzahl);
  uint16_t wert = Wire.read() | (Wire.read() << 8);
  return ~wert;  // Taster schalten nach GND
}

Du musst jetzt nur drauf achten, dass Du HM auf dem richtigen USB-Port lauschen lässt.
Dann kannst Du den SerMon für den KINg benutzen und im zweiten Fenster mitverfolgen ob das was Du willst auch passiert.

BIn dann auch weg :-)

1 Like

Gelegendlich muß etwas Kultur sein und wenn es nur 30 Minuten Weihnachtslieder auf einer Kirchenorgel sind. Ist eigentlich für Kinder, aber Erwachsene dürfen auch lauschen :smiley:

Da könnte man noch etwas abstrahieren:

#define SerMon Serial
#define MidiA  Serial1
#define MidiB  Serial2

Dann weiß man gleich, was gemeint ist. OK?

Programm
/* Belegung Mega2560
        CIPO = 50   unbenutzt (früher MISO)
DATA  = COPI = 51   Pin von Hardware-SPI (früher MOSI)
CLOCK = SCA  = 52   Pin von Hardware-SPI
LATCH = CS   = 53   Chip Select Pin
OE           = 49   Output Enable, frei wählbarer Pin
*/
/* Usecase by noiasca 2025-11-16 3870/218 in tlc.h */
#define agmue
#define SerMon Serial
#define MidiA  Serial1
#define MidiB  Serial2
#include <Wire.h>
// EXPANDER (PCF8575)----------------------------------------------------------------------------------------------
constexpr uint8_t inputPins{ 16 };            // Anzahl Pins am PCF8575
constexpr uint8_t kOff{ 143 };                // Kanaloffset
constexpr uint32_t pcfMessIntervall{ 5000 };  // in µs
uint32_t pcfVorhin;

#include "gruppen.h"
#include "tlc.h"
//
void outMidiTaste(const byte idx, const byte val) {
  MidiA.write(kOff + taste[idx].midiAKanal);
  MidiA.write(taste[idx].midiANote);
  MidiA.write(val);
  MidiB.write(kOff + taste[idx].midiBKanal);
  MidiB.write(taste[idx].midiBNote);
  MidiB.write(val);
}
//
void printMidiTaste(const byte idx, const byte val) {
  SerMon.print(idx);
  SerMon.print('\t');
  SerMon.print(taste[idx].led);
  SerMon.print('\t');
  SerMon.print(kOff + taste[idx].midiAKanal);
  SerMon.print('\t');
  SerMon.print(taste[idx].midiANote);
  SerMon.print('\t');
  SerMon.print(val);
  SerMon.print('\t');
  SerMon.print(kOff + taste[idx].midiBKanal);
  SerMon.print('\t');
  SerMon.print(taste[idx].midiBNote);
  SerMon.print('\t');
  SerMon.println(val);
}
//
uint16_t read16(uint8_t adresse) {
  const uint8_t anzahl = 2;
  Wire.requestFrom(adresse, anzahl);
  uint16_t wert = Wire.read() | (Wire.read() << 8);
  return ~wert;  // Taster schalten nach GND
}
//
class PCF_Board {
  const uint8_t i2cAdd;
  const uint8_t pcfNum;
  uint16_t wertAlt = 0;
public:
  PCF_Board(const uint8_t i2cAdd, const uint8_t pcfNum)
    : i2cAdd(i2cAdd), pcfNum(pcfNum){};

  void init() {
    wertAlt = read16(i2cAdd);
  }
  //
  void auswertung() {
    uint16_t wert = read16(i2cAdd);
    uint16_t diff = wertAlt ^ wert;
    wertAlt = wert;
    if (diff) {
      for (byte j = 0; j < inputPins; j++) {
        if (bitRead(diff, j)) {
          byte idx = j + pcfNum * inputPins;
          if (bitRead(wert, j)) {
            printMidiTaste(idx, 127);
            outMidiTaste(idx, 127);
            if (grp[taste[idx].grpIDX].mode != 'B') {
              for (uint8_t j = grp[taste[idx].grpIDX].ersteLED; j <= grp[taste[idx].grpIDX].letzteLED; j++) {
                tlc.off(j);
              }
            }
            tlc.toggle(taste[idx].led);
          } else {
            printMidiTaste(idx, 0);
            outMidiTaste(idx, 0);
          }
        }
      }
      tlc.update();
    }
  }
};
#ifdef agmue
PCF_Board pcfBoard[]{ { 0x3A, 0 }, { 0x3B, 1 }, { 0x3C, 2 }, { 0x38, 3 }, { 0x20, 4 }, { 0x21, 5 } };
#else
PCF_Board pcfBoard[]{ { 0x20, 0 }, { 0x21, 1 }, { 0x22, 2 }, { 0x23, 3 }, { 0x24, 4 }, { 0x25, 5 } };
#endif
//
void loop() {
  uint32_t jetzt = micros();

  if (jetzt - pcfVorhin >= pcfMessIntervall) {
    pcfVorhin = jetzt;
    for (PCF_Board &p : pcfBoard) p.auswertung();
  }
}
//
void setup() {
  SerMon.begin(115200);  // Texte und Upload
  MidiA.begin(38400);  // MIDI HairlessMIDI mit 38400, Standard MIDI mit 31250
  MidiB.begin(38400);  // MIDI HairlessMIDI mit 38400, Standard MIDI mit 31250
  delay(1000);

  Wire.begin();
  Wire.setClock(400000);
  for (PCF_Board &p : pcfBoard) p.init();

  SPI.begin();
  tlc.start();
  tlc.update();
  pinMode(pinOE, OUTPUT);
  digitalWrite(pinOE, LOW);

  SerMon.println("\nStart ...");
}

Ja, so meine derzeitige Vorstellung. Die Abfrage if (grp[taste[idx].grpIDX].mode != 'B') { unterscheidet 'B' von den anderen, aber 'A' und 'C' sind erstmal gleich. Wenn beispielsweise Gruppe O nach 'C' umgeschaltet werden soll, muß auch Gruppe N nach 'C' umgeschaltet werden. Jetzt weiß ich nur nicht, was passieren soll, wenn Gruppe O nach 'A' oder 'B' umgeschaltet wird, was passiert dann mit Gruppe N?

Eventuell genügt es auch, wenn Mode C nur bedeutet, zwei Gruppen nach 'A' zu schalten. Oder oh Schreck sollen im Mode C die Gruppen O und N gar als eine Gruppe funktionieren? Das hatte ich noch überhaupt nicht erwogen :face_with_peeking_eye:

Außerdem hadere ich noch mit dem langen Tastendruck. GROUP ML1 hat keinen MIDI-Inhalt, ein kurzer Druck tut also nichts. Dann wäre doch auch eine Funktion wie bei der Shift-Taste denkbar?

Was ist mit der Helligkeit, die soll sich vermutlich direkt ändern, wenn GROUP A oder B gedrückt wird.

Oder habe ich was nicht verstanden?

1 Like
auto &SerMon = Serial;

void setup()
{
  SerMon.begin(115200);
  SerMon.println(F("\r\nStart...\r\n"));
}

void loop()
{}

:slight_smile:

1 Like

Ja genau so

Ich habe im MQ die Möglichkeit beliebig viele und beliebig große Gruppen zu bilden.
Mit der BUSQUEEN hab ich z.B. eine Gruppe aus 12 . Diese' nur ein aus Gruppe ist an" Funktion hat den Vorteil ich muss nicht Ausschalten, was vorher an ist. 12 ist mir manchmal aber zu knapp.
Gruppe O hat ja schon 14, das könnte aber auch knapp werden wenn ich mich austobe beim Programmieren und Zeit dafür habe.
Darum möchte ich ggf. die Möglichkeit haben, die Gruppe O mit N zu einer Gruppe kombinieren.
Manchmal habe ich aber auch specials die dann auf N kommen und dann brauche ich O und N jeweils eigenständig.

Nun klar?

Wenn O von C auf A oder B geschaltet wird.
Dann Schalte N auf das gleiche, fals es anders gebraucht wird ,lässt rs sich ja schnell umschalten.

Noch nicht.
Doch auch da benötige ich auch unbedingt MIDI NOTE Befehle, genau wie bei all den anderen Tasten. Bei der Gruppe ist es nur nicht Zeitkritisch, da ist es im gegensatz zu ALLEN ANDEREN TASTEN völlig ok. Wenn der Befehl erst mit loslassen kommt.
Das ist ja aich der Grund, warum ich diese Taster auf dem MEGA von den anderen getrennt habe. Auf den PCF wären ja sonst auch 8 Tasten noch frei.

Ich stelle es mir so vor:
Taste unter eine Sekunde gedrückt sende MIDI NOTE . Länger als 1Sek gedrückt, gehe in den Long Mode und schau welche Taste gedrückt wird und so wirkt es dann wie eine Shift Taste. Hat aber normale Funktion.

LG Rebecca

#define agmue
auto &SerMon = Serial;
auto &MidiA  = Serial1;
auto &MidiB  = Serial2;
#include <Wire.h>
Programm
/* Belegung Mega2560
        CIPO = 50   unbenutzt (früher MISO)
DATA  = COPI = 51   Pin von Hardware-SPI (früher MOSI)
CLOCK = SCA  = 52   Pin von Hardware-SPI
LATCH = CS   = 53   Chip Select Pin
OE           = 49   Output Enable, frei wählbarer Pin
*/
/* Usecase by noiasca 2025-11-16 3870/218 in tlc.h */
#define agmue
auto &SerMon = Serial;
auto &MidiA  = Serial1;
auto &MidiB  = Serial2;
#include <Wire.h>
// EXPANDER (PCF8575)----------------------------------------------------------------------------------------------
constexpr uint8_t inputPins{ 16 };            // Anzahl Pins am PCF8575
constexpr uint8_t kOff{ 143 };                // Kanaloffset
constexpr uint32_t pcfMessIntervall{ 5000 };  // in µs
uint32_t pcfVorhin;

#include "gruppen.h"
#include "tlc.h"
//
void outMidiTaste(const byte idx, const byte val) {
  MidiA.write(kOff + taste[idx].midiAKanal);
  MidiA.write(taste[idx].midiANote);
  MidiA.write(val);
  MidiB.write(kOff + taste[idx].midiBKanal);
  MidiB.write(taste[idx].midiBNote);
  MidiB.write(val);
}
//
void printMidiTaste(const byte idx, const byte val) {
  SerMon.print(idx);
  SerMon.print('\t');
  SerMon.print(taste[idx].led);
  SerMon.print('\t');
  SerMon.print(kOff + taste[idx].midiAKanal);
  SerMon.print('\t');
  SerMon.print(taste[idx].midiANote);
  SerMon.print('\t');
  SerMon.print(val);
  SerMon.print('\t');
  SerMon.print(kOff + taste[idx].midiBKanal);
  SerMon.print('\t');
  SerMon.print(taste[idx].midiBNote);
  SerMon.print('\t');
  SerMon.println(val);
}
//
uint16_t read16(uint8_t adresse) {
  const uint8_t anzahl = 2;
  Wire.requestFrom(adresse, anzahl);
  uint16_t wert = Wire.read() | (Wire.read() << 8);
  return ~wert;  // Taster schalten nach GND
}
//
class PCF_Board {
  const uint8_t i2cAdd;
  const uint8_t pcfNum;
  uint16_t wertAlt = 0;
public:
  PCF_Board(const uint8_t i2cAdd, const uint8_t pcfNum)
    : i2cAdd(i2cAdd), pcfNum(pcfNum){};

  void init() {
    wertAlt = read16(i2cAdd);
  }
  //
  void auswertung() {
    uint16_t wert = read16(i2cAdd);
    uint16_t diff = wertAlt ^ wert;
    wertAlt = wert;
    if (diff) {
      for (byte j = 0; j < inputPins; j++) {
        if (bitRead(diff, j)) {
          byte idx = j + pcfNum * inputPins;
          if (bitRead(wert, j)) {
            printMidiTaste(idx, 127);
            outMidiTaste(idx, 127);
            if (grp[taste[idx].grpIDX].mode != 'B') {
              for (uint8_t j = grp[taste[idx].grpIDX].ersteLED; j <= grp[taste[idx].grpIDX].letzteLED; j++) {
                tlc.off(j);
              }
            }
            tlc.toggle(taste[idx].led);
          } else {
            printMidiTaste(idx, 0);
            outMidiTaste(idx, 0);
          }
        }
      }
      tlc.update();
    }
  }
};
#ifdef agmue
PCF_Board pcfBoard[]{ { 0x3A, 0 }, { 0x3B, 1 }, { 0x3C, 2 }, { 0x38, 3 }, { 0x20, 4 }, { 0x21, 5 } };
#else
PCF_Board pcfBoard[]{ { 0x20, 0 }, { 0x21, 1 }, { 0x22, 2 }, { 0x23, 3 }, { 0x24, 4 }, { 0x25, 5 } };
#endif
//
void loop() {
  uint32_t jetzt = micros();

  if (jetzt - pcfVorhin >= pcfMessIntervall) {
    pcfVorhin = jetzt;
    for (PCF_Board &p : pcfBoard) p.auswertung();
  }
}
//
void setup() {
  SerMon.begin(115200);  // Texte und Upload
  MidiA.begin(38400);  // MIDI HairlessMIDI mit 38400, Standard MIDI mit 31250
  MidiB.begin(38400);  // MIDI HairlessMIDI mit 38400, Standard MIDI mit 31250
  delay(1000);

  Wire.begin();
  Wire.setClock(400000);
  for (PCF_Board &p : pcfBoard) p.init();

  SPI.begin();
  tlc.start();
  tlc.update();
  pinMode(pinOE, OUTPUT);
  digitalWrite(pinOE, LOW);

  SerMon.println("\nStart ...");
}

:smiley:

Moin, ja!

2 Likes

Ich bin etwas irritiert und hätte gerne eine Bestätigung, das es so soll.

Und:
geht es auch wenn ich das Paralell Anschließe?
dann bau ich einen Steckadapter, der dazwischen kommt und ich dann beide MIDI OUTS und den MEGA B gleichzeitig dran habe.

Wenn das leichter für euch ist, kann ich auch nur den Taster ML1 auf dem MEGA belassen und die anderen 7 GROUP Taster mit auf die PCF bringen. Wie gesagt ich hab viel mit Steckern gemacht und kann mit relativ wenig Aufwand ändern.

Ich habe mal damit getestet:

	{ 0, 21, 'A'},  // 1: Gruppe O    // MODE C (Beide O & N  auf { 0, 21, 'A'} setzen)
	{ 0, 21, 'A'},  // 2: Gruppe N    // MODE C
//	{ 0, 13, 'A'},  // 1: Gruppe O  // MODE A oder B
//	{14, 21, 'A'},  // 2: Gruppe N  // MODE A oder B

Mit dieser Einstellung bekomme ich die Einstellung für Mode C hin, so Funktioniert es genau wie ich es für C möchte.

Und sorry, das hatte ich noch nicht eingebracht:
Es kann sein, das ich auch eine Gruppe auch mal als reine Taster im MQ (LICHTPROGRAMM) definiere, da wäre es dann wiederum nicht gut, wenn die lampen an bleiben.
Somit benötige ich auch den MODE D:
MODE D leuchtet nur, wenn der Taster gedrückt wird, nach dem Loslassen ist auch die LED aus.

Was irritiert?

Die beiden MIDI-Abgönge TX gehen auf den "neuen" Mega jeweils RX.

Ziel:

Der rudimentäre Code fragt beide Kanäle ab und gibt auf der USB-Schnittstelle beide Schnittstelleninfos aus.
Um sicher zu gehen, dass alle zusammenhängenden Informationen auf einer Schnittstelle gelesen werden, wird auf ein Timeout von derzeit 5ms nach dem letzten Byte gewartet.

Wenn der Zug nachher leerer ist, kann ich ggfls. auch noch mehr machen, aber erstmal würde mir reichen wenn HM was ausgibt :slight_smile:

1 Like

es gab auch
Ja
nein
vieleicht

das war mir auch noch unklar

1 Like

Das sollte funktionieren, verhält sich dann wie thru.

1 Like
Programm experimentell für die Mode-Umschaltung
/* Belegung Mega2560
        CIPO = 50   unbenutzt (früher MISO)
DATA  = COPI = 51   Pin von Hardware-SPI (früher MOSI)
CLOCK = SCA  = 52   Pin von Hardware-SPI
LATCH = CS   = 53   Chip Select Pin
OE           = 49   Output Enable, frei wählbarer Pin
*/
/* Usecase by noiasca 2025-11-16 3870/218 in tlc.h */
#define agmue
auto &SerMon = Serial;
auto &MidiA = Serial1;
auto &MidiB = Serial2;
#include <Wire.h>
// EXPANDER (PCF8575)----------------------------------------------------------------------------------------------
constexpr uint8_t inputPins{ 16 };            // Anzahl Pins am PCF8575
constexpr uint8_t kOff{ 143 };                // Kanaloffset
constexpr uint32_t pcfMessIntervall{ 5000 };  // in µs
uint32_t pcfVorhin;

#include "gruppen.h"
#include "tlc.h"
//
void outMidiTaste(const byte idx, const byte val) {
  MidiA.write(kOff + pcfTaste[idx].midiAKanal);
  MidiA.write(pcfTaste[idx].midiANote);
  MidiA.write(val);
  MidiB.write(kOff + pcfTaste[idx].midiBKanal);
  MidiB.write(pcfTaste[idx].midiBNote);
  MidiB.write(val);
}
//
void printMidiTaste(const byte idx, const byte val) {
  SerMon.print(idx);
  SerMon.print('\t');
  SerMon.print(pcfTaste[idx].led);
  SerMon.print('\t');
  SerMon.print(kOff + pcfTaste[idx].midiAKanal);
  SerMon.print('\t');
  SerMon.print(pcfTaste[idx].midiANote);
  SerMon.print('\t');
  SerMon.print(val);
  SerMon.print('\t');
  SerMon.print(kOff + pcfTaste[idx].midiBKanal);
  SerMon.print('\t');
  SerMon.print(pcfTaste[idx].midiBNote);
  SerMon.print('\t');
  SerMon.println(val);
}
//
uint16_t read16(uint8_t adresse) {
  const uint8_t anzahl = 2;
  Wire.requestFrom(adresse, anzahl);
  uint16_t wert = Wire.read() | (Wire.read() << 8);
  return ~wert;  // Taster schalten nach GND
}
//
class PCF_Board {
  const uint8_t i2cAdd;
  const uint8_t pcfNum;
  uint16_t wertAlt = 0;
public:
  PCF_Board(const uint8_t i2cAdd, const uint8_t pcfNum)
    : i2cAdd(i2cAdd), pcfNum(pcfNum){};

  void init() {
    wertAlt = read16(i2cAdd);
  }
  //
  void auswertung() {
    uint16_t wert = read16(i2cAdd);
    uint16_t diff = wertAlt ^ wert;
    wertAlt = wert;
    if (diff) {
      for (byte j = 0; j < inputPins; j++) {
        if (bitRead(diff, j)) {
          byte idx = j + pcfNum * inputPins;
          if (taste[5].longpress()) {
            if (        (idx == 40) || (idx == 48) || (idx == 80) ) {
                grp[pcfTaste[idx].grpIDX].mode = 'A';
            } else if ( (idx == 41) || (idx == 49) || (idx == 81) ) {
                grp[pcfTaste[idx].grpIDX].mode = 'B';
            } else if ( (idx == 56) || (idx == 64) ) {
                grp[pcfTaste[idx].grpIDX].mode = 'A';
                if (grp[pcfTaste[56].grpIDX].mode == 'C') grp[pcfTaste[56].grpIDX].mode = 'A';
                if (grp[pcfTaste[64].grpIDX].mode == 'C') grp[pcfTaste[64].grpIDX].mode = 'A';
            } else if ( (idx == 57) || (idx == 65) ) {
                grp[pcfTaste[idx].grpIDX].mode = 'B';
                if (grp[pcfTaste[57].grpIDX].mode == 'C') grp[pcfTaste[57].grpIDX].mode = 'B';
                if (grp[pcfTaste[65].grpIDX].mode == 'C') grp[pcfTaste[65].grpIDX].mode = 'B';
            } else if ( (idx == 58) || (idx == 66) ) {
                grp[pcfTaste[58].grpIDX].mode = 'C';
                grp[pcfTaste[66].grpIDX].mode = 'C';
            }
            SerMon.print("longPress idx "); SerMon.print(idx); SerMon.print("\tgrpIDX "); SerMon.print(pcfTaste[idx].grpIDX); SerMon.print("\tMode "); SerMon.println(grp[pcfTaste[idx].grpIDX].mode); 
          } else {
            if (bitRead(wert, j)) {
              printMidiTaste(idx, 127);
              outMidiTaste(idx, 127);
              SerMon.print("\tMode "); SerMon.println(grp[pcfTaste[idx].grpIDX].mode);
              if (grp[pcfTaste[idx].grpIDX].mode == 'C') {
                for (uint8_t j = grp[1].ersteLED; j <= grp[2].letzteLED; j++) {
                  tlc.off(j);
                }
              } else if (grp[pcfTaste[idx].grpIDX].mode != 'B') {
                for (uint8_t j = grp[pcfTaste[idx].grpIDX].ersteLED; j <= grp[pcfTaste[idx].grpIDX].letzteLED; j++) {
                  tlc.off(j);
                }
              }
              tlc.toggle(pcfTaste[idx].led);
            } else {
              printMidiTaste(idx, 0);
              outMidiTaste(idx, 0);
            }
          }
        }
      }
      tlc.update();
    }
  }
};
#ifdef agmue
PCF_Board pcfBoard[]{ { 0x3A, 0 }, { 0x3B, 1 }, { 0x3C, 2 }, { 0x20, 3 }, { 0x21, 4 }, { 0x38, 5 } };
#else
PCF_Board pcfBoard[]{ { 0x20, 0 }, { 0x21, 1 }, { 0x22, 2 }, { 0x23, 3 }, { 0x24, 4 }, { 0x25, 5 } };
#endif
//
void loop() {
  for (Taste &t : taste) t.update();
  uint32_t jetzt = micros();

  if (jetzt - pcfVorhin >= pcfMessIntervall) {
    pcfVorhin = jetzt;
    for (PCF_Board &p : pcfBoard) p.auswertung();
  }
}
//
void setup() {
  SerMon.begin(115200);  // Texte und Upload
  MidiA.begin(38400);    // MIDI HairlessMIDI mit 38400, Standard MIDI mit 31250
  MidiB.begin(38400);    // MIDI HairlessMIDI mit 38400, Standard MIDI mit 31250
  delay(1000);

  Wire.begin();
  Wire.setClock(400000);
  for (Taste t : taste) t.init();
  for (PCF_Board &p : pcfBoard) p.init();

  SPI.begin();
  tlc.start();
  tlc.update();
  pinMode(pinOE, OUTPUT);
  digitalWrite(pinOE, LOW);

  SerMon.println("\nStart ...");
}
gruppen.h
struct Taste {
  const uint8_t pin;         // Pin am Mega2560
  const uint8_t midiAKanal;  // MIDI A Kanal  NOTE ON auf KANAL1 =1 Kanal2 =2 etc.
  const uint8_t midiANote;   // MIDI A Note   0-127
  const uint8_t midiBKanal;  // MIDI B Kanal
  const uint8_t midiBNote;   // MIDI B Note
  uint32_t vorhin;           // gemerkte Zeit
  bool pressState;           // long == true
  bool aktZustand;
  bool altZustand;
  uint8_t schritt;

  Taste(const uint8_t pin, const uint8_t midiAKanal, const uint8_t midiANote, const uint8_t midiBKanal, const uint8_t midiBNote)
    : pin(pin), midiAKanal(midiAKanal), midiANote(midiANote), midiBKanal(midiBKanal), midiBNote(midiBNote), vorhin(0), pressState(0), aktZustand(0), schritt(0){};

  void init() {
    pinMode(pin, INPUT_PULLUP);
  }

  bool longpress() {
    bool tmp = pressState;
    pressState = false;
    return tmp;
  }

  void update() {
    aktZustand = digitalRead(pin);
    switch (schritt) {
      case 0:
       if ((aktZustand != altZustand) & (!aktZustand)) {
          vorhin = millis();
          pressState = false;
          schritt++;
        }
        break;
      case 1:
        if (millis() - vorhin >= 500) {
          if (aktZustand) {
            schritt = 3;  // short
          } else {
            schritt++;
          }
        }
        break;
      case 2:
        if (aktZustand) {
          schritt = 3;  // short
        } else {
          if (millis() - vorhin >= 1000) {
            pressState = true;
            schritt = 3;
          }
        }
        break;
        case 3:
          if (millis() - vorhin >= 2000) {
            pressState = true;
            schritt = 0;
          }
        break;
    }
    altZustand = aktZustand;
  }
};

Taste taste[]{
	// Mega2560
  //pin, MIDI NoteA, MIDI NoteB                   
  { 22,  1,   0,  1,   0 },  // GROUP A     Helligkeit LED -
  { 23,  1,   0,  1,   0 },  // GROUP B     Helligkeit LED +
  { 24,  1,   0,  1,   0 },  // GROUP C
  { 25,  1,   0,  1,   0 },  // GROUP D
  { 26,  1,   0,  1,   0 },  // GROUP ALL
  { 27,  1,   0,  1,   0 },  // GROUP ML1   LONG für den MODE Wechsel
  { 28,  1,   0,  1,   0 },  // GROUP ML2
  { 29,  1,   0,  1,   0 },  // GROUP FLOOD
};

struct Gruppe {
  const uint8_t ersteLED;     // erste LED der Gruppe
  const uint8_t letzteLED;    // letzte LED der Gruppe
  char mode;
};

Gruppe grp[] {
	{99, 99, 'A'},  // 0: ohne Gruppe
	{ 0, 13, 'A'},  // 1: Gruppe O
	{14, 21, 'A'},  // 2: Gruppe N
	{24, 35, 'A'},  // 3: Gruppe Chaser
	{36, 41, 'A'},  // 4: Gruppe POSITIONS
	{42, 47, 'A'},  // 5: Gruppe M
};

struct PCF_Taste {
  const uint8_t led;          // LED-Nummer 0 bis 47
  const uint8_t grpIDX;       // Gruppenindex
  const uint8_t midiAKanal;   // MIDI A Kanal  NOTE ON auf KANAL1 =1 Kanal2 =2 etc.
  const uint8_t midiANote;    // MIDI A Note   0-127
  const uint8_t midiBKanal;   // MIDI B Kanal
  const uint8_t midiBNote;    // MIDI B Note
};

PCF_Taste pcfTaste[]{
	// PCF EXPANDER A
  //LED\GRP\MIDI NoteA\MIDI NoteB        idx
  { 255,  0,  1,   0,  1,   0 },  // P00 00   COLOR WIPE 1
  { 255,  0,  1,   1,  1,   1 },  // P01 01   COLOR UV
  { 255,  0,  1,   2,  1,   2 },  // P02 02   COLOR WIPE 2
  { 255,  0,  1,   3,  1,   3 },  // P03 03   COLOR CHASE
  { 255,  0,  1,   4,  1,   4 },  // P04 04   COLOR BLUE
  { 255,  0,  1,   5,  1,   5 },  // P05 05   COLOR PINK
  { 255,  0,  1,   6,  1,   6 },  // P06 06   COLOR GREEN
  { 255,  0,  1,   7,  1,   7 },  // P07 07   COLOR CYAN
  { 255,  0,  1,   8,  1,   8 },  // P10 08   COLOR RED
  { 255,  0,  1,   9,  1,   9 },  // P11 09   COLOR WHITE
  { 255,  0,  1,  10,  1,  10 },  // P12 10   COLOR ORANGE
  { 255,  0,  1,  11,  1,  11 },  // P13 11   COLOR YELLOW
  { 255,  0,  1,  34,  1,  34 },  // P14 12   FLASH 17
  { 255,  0,  1,  35,  1,  35 },  // P15 13   FLASH 18
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
	// PCF EXPANDER B
  { 255,  0,  1,  18,  1,  18 },  // P00 16    FLASH 1
  { 255,  0,  1,  19,  1,  19 },  // P01 17    FLASH 2
  { 255,  0,  1,  20,  1,  20 },  // P02 18    FLASH 3
  { 255,  0,  1,  21,  1,  21 },  // P03 19    FLASH 4
  { 255,  0,  1,  22,  1,  22 },  // P04 20    FLASH 5
  { 255,  0,  1,  23,  1,  23 },  // P05 21    FLASH 6
  { 255,  0,  1,  24,  1,  24 },  // P06 22    FLASH 7
  { 255,  0,  1,  25,  1,  25 },  // P07 23    FLASH 8
  { 255,  0,  1,  26,  1,  26 },  // P10 24    FLASH 9
  { 255,  0,  1,  27,  1,  27 },  // P11 25    FLASH 10
  { 255,  0,  1,  28,  1,  28 },  // P12 26    FLASH 11
  { 255,  0,  1,  29,  1,  29 },  // P13 27    FLASH 12
  { 255,  0,  1,  30,  1,  30 },  // P14 28    FLASH 13
  { 255,  0,  1,  31,  1,  31 },  // P15 29    FLASH 14
  { 255,  0,  1,  32,  1,  32 },  // P16 30    FLASH 15
  { 255,  0,  1,  33,  1,  33 },  // P17 31    FLASH 16
	// PCF EXPANDER C                PIN idx   GRP           MODE    LED
  { 255,  0,  1,  53,  1,  53 },  // P00 32    GOBO OPEN
  { 255,  0,  1,  53,  1,  53 },  // P01 33    GOBO I
  { 255,  0,  1,  53,  1,  53 },  // P02 34    GOBO II
  { 255,  0,  1,  53,  1,  53 },  // P03 35    GOBO III
  { 255,  0,  1,  53,  1,  53 },  // P04 36    GOBO IV
  { 255,  0,  1,  53,  1,  53 },  // P05 37    GOBO V
  { 255,  0,  1,  53,  1,  53 },  // P06 38    GOBO VI
  { 255,  0,  1,  53,  1,  53 },  // P07 39    GOBO Wheel rotation
  {  36,  4,  1,  53,  1,  53 },  // P10 40    POSITIONS I   MODE A  36
  {  37,  4,  1,  53,  1,  53 },  // P11 41    POSITIONS II  MODE B  37
  {  38,  4,  1,  53,  1,  53 },  // P12 42    POSITIONS III         38
  {  39,  4,  1,  53,  1,  53 },  // P13 43    POSITIONS IV          39
  {  40,  4,  1,  53,  1,  53 },  // P14 44    POSITIONS V           40
  {  41,  4,  1,  53,  1,  53 },  // P15 45    POSITIONS VI          41
  { 255,  0,  1,  53,  1,  53 },  // P16 46    TEMPO TAP
  { 255,  0,  1,  53,  1,  53 },  // P17 47    Playlight Reset
	// PCF EXPANDER D                PIN     GRP           MODE    LED
  {  42,  5,  1,  53,  1,  53 },  // P00 48    M1            MODE A  42
  {  43,  5,  1,  53,  1,  53 },  // P01 49    M2            MODE B  43
  {  44,  5,  1,  53,  1,  53 },  // P02 50    M3                    44
  {  45,  5,  1,  53,  1,  53 },  // P03 51    M4                    45
  {  46,  5,  1,  53,  1,  53 },  // P04 52    M5                    46
  {  47,  5,  1,  53,  1,  53 },  // P05 53    M6                    47
  { 255,  0,  1,  53,  1,  53 },  // P06 54    PRELOAD        PWM 2(MEGA)
  {  22,  0,  1,  53,  1,  53 },  // P07 55    STROBE                22
  {  14,  2,  1,  53,  1,  53 },  // P10 56    N 1           MODE A  14
  {  15,  2,  1,  53,  1,  53 },  // P11 57    N 2           MODE B  15
  {  16,  2,  1,  53,  1,  53 },  // P12 58    N 3           MODE C  16
  {  17,  2,  1,  53,  1,  53 },  // P13 59    N 4                   17
  {  18,  2,  1,  53,  1,  53 },  // P14 60    N 5                   18
  {  19,  2,  1,  53,  1,  53 },  // P15 61    N 6                   19
  {  20,  2,  1,  53,  1,  53 },  // P16 62    N 7                   20
  {  21,  2,  1,  53,  1,  53 },  // P17 63    N 8                   21
	// PCF EXPANDER E                PIN     GRP           MODE    LED
  {   0,  1,  1,  53,  1,  53 },  // P00 64    O 1           MODE A   0
  {   1,  1,  1,  53,  1,  53 },  // P01 65    O 2           MODE B   1
  {   2,  1,  1,  44,  1,  44 },  // P02 66    O 3           MODE C   2
  {   3,  1,  1,  45,  1,  45 },  // P03 67    O 4                    3
  {   4,  1,  1,  46,  1,  46 },  // P04 68    O 5                    4
  {   5,  1,  1,  47,  1,  47 },  // P05 69    O 6                    5
  {   6,  1,  1,  10,  1,  10 },  // P06 70    O 7                    6
  {   7,  1,  1,  11,  1,  11 },  // P07 71    O 8                    7
  {   8,  1,  1,  31,  1,  27 },  // P10 72    O 9                    8
  {   9,  1,  1,  32,  1,  28 },  // P11 73    O 10                   9
  {  10,  1,  1,  33,  1,  32 },  // P12 74    O 11                  10
  {  11,  1,  1,  34,  1,  27 },  // P13 75    O 12                  11
  {  12,  1,  1,  35,  1,  35 },  // P14 76    O 13                  12
  {  13,  1,  1,  36,  1,  58 },  // P15 77    O 14                  13
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
	// PCF EXPANDER F                PIN     GRP           MODE    LED
  {  24,  3,  1,   0,  1,   0 },  // P00 80    Chaser 1      MODE A   24
  {  25,  3,  1,   1,  1,   1 },  // P01 81    Chaser 2      MODE B   25
  {  26,  3,  1,   2,  1,   2 },  // P02 82    Chaser 3               26
  {  27,  3,  1,   3,  1,   3 },  // P03 83    Chaser 4               27
  {  28,  3,  1,   4,  1,   4 },  // P04 84    Chaser 5               28
  {  29,  3,  1,   5,  1,   5 },  // P05 85    Chaser 6               29
  {  30,  3,  1,   6,  1,   6 },  // P06 86    Chaser 7               30
  {  31,  3,  1,   7,  1,   7 },  // P07 87    Chaser 8               31
  {  32,  3,  1,   8,  1,   8 },  // P10 88    Chaser 9               32
  {  33,  3,  1,   9,  1,   9 },  // P11 89    Chaser 10              33
  {  34,  3,  1,  10,  1,  10 },  // P12 90    Chaser 11              34
  {  35,  3,  1,  11,  1,  11 },  // P13 91    Chaser 12(OFF)         35
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
};
tlc.h (unverändert)
#include <TLC5947_SPI.hpp>

constexpr uint8_t DEVICES{ 2 };    // Anzahl der angeschlossenen Platinen mit je 24 LEDs.
constexpr uint8_t pinLATCH{ 53 };  // Pin für die Datenübernahme.
constexpr uint8_t pinOE{ 49 };     // Pin für die Aktivierung der LED-Treiber (O̲utput E̲nable).

class MyTLC : public TLC5947_SPI {
protected:
  static const uint8_t numPWM = 4;
  const uint16_t valuesPWM[numPWM] = { 50, 200, 1000, 4095 };
  uint8_t stepPWM = numPWM - 3;
  uint16_t offPWM = 0;  // PWM for off state. When you set this value >0 the LED will be slightly dimmed also in off state.

public:
  MyTLC(const uint8_t latch, const uint8_t deviceCount = 1, const uint32_t SPIclock = 8000000UL, SPIClass &spi = SPI)
    : TLC5947_SPI(latch, deviceCount, SPIclock, spi) {}

  // call this function in setup().
  // it can't be called .begin() as there is already a .begin() in the parent class but the parent class has no late binding (not virtual), hence we name it .start()
  void start() {
    begin();  // the parent class needs a .begin()
    setAll(offPWM);
  }

  // switch on
  void on(const uint8_t channel) {
    setPWM(channel, valuesPWM[stepPWM]);
  }

  // switch off
  void off(const uint8_t channel) {
    setPWM(channel, offPWM);
  }

  // toggle
  void toggle(const uint8_t channel) {
    if (channel < (DEVICES * 24)) {
      if (getPWM(channel) != offPWM) {
        off(channel);
      } else {
        on(channel);
      }
    }
  }

  // switch to day lighter mode
  void
  lighterPWM() {
    if (stepPWM < numPWM - 1) {
      stepPWM++;
      modifyActive(valuesPWM[stepPWM]);
    }
  }

  // switch to day darker mode
  void darkerPWM() {
    if (stepPWM > 0) {
      stepPWM--;
      modifyActive(valuesPWM[stepPWM]);
    }
  }

  // change PWM of active channels
  void modifyActive(const uint16_t newPWM) {
    for (byte channel = 0; channel < getChannels(); channel++) {
      if (getPWM(channel) != offPWM) {
        setPWM(channel, newPWM);
      }
    }
  }
};
MyTLC tlc(pinLATCH, DEVICES);  // create an instance of your new class

Mode D ist nicht berücksichtigt.

Bitte testen und berichten! Bin jetzt aber wieder bei einer Kulturveranstaltung.

1 Like

Ich sag nur WOW.

kaum sag ich etwas , schon drin...
bin echt ... hui ... :orange_heart:

Das Umschalten über Änderungen in der gruppen.h funktioniert EINWANDFREI.

BITTE ab jetzt für Änderungen diese gruppen.h verwenden, da ich hier jetzt schon einiges an MIDI Einstellungen eingepflegt habe.
AKTUELLE gruppen.h

struct Taste {
  const uint8_t pin;         // Pin am Mega2560
  const uint8_t midiAKanal;  // MIDI A Kanal  NOTE ON auf KANAL1 =1 Kanal2 =2 etc.
  const uint8_t midiANote;   // MIDI A Note   0-127
  const uint8_t midiBKanal;  // MIDI B Kanal
  const uint8_t midiBNote;   // MIDI B Note
  uint32_t vorhin;           // gemerkte Zeit
  bool pressState;           // long == true
  bool aktZustand;
  bool altZustand;
  uint8_t schritt;

  Taste(const uint8_t pin, const uint8_t midiAKanal, const uint8_t midiANote, const uint8_t midiBKanal, const uint8_t midiBNote)
    : pin(pin), midiAKanal(midiAKanal), midiANote(midiANote), midiBKanal(midiBKanal), midiBNote(midiBNote), vorhin(0), pressState(0), aktZustand(0), schritt(0){};

  void init() {
    pinMode(pin, INPUT_PULLUP);
  }

  bool longpress() {
    bool tmp = pressState;
    pressState = false;
    return tmp;
  }

  void update() {
    aktZustand = digitalRead(pin);
    switch (schritt) {
      case 0:
       if ((aktZustand != altZustand) & (!aktZustand)) {
          vorhin = millis();
          pressState = false;
          schritt++;
        }
        break;
      case 1:
        if (millis() - vorhin >= 500) {
          if (aktZustand) {
            schritt = 3;  // short
          } else {
            schritt++;
          }
        }
        break;
      case 2:
        if (aktZustand) {
          schritt = 3;  // short
        } else {
          if (millis() - vorhin >= 1000) {
            pressState = true;
            schritt = 3;
          }
        }
        break;
        case 3:
          if (millis() - vorhin >= 2000) {
            pressState = true;
            schritt = 0;
          }
        break;
    }
    altZustand = aktZustand;
  }
};

Taste taste[]{
	// Mega2560
  //pin, MIDI NoteA, MIDI NoteB                   
  { 22,  2,   60,  1,   0 },  // GROUP A     Helligkeit LED -   E2.7
  { 23,  2,   61,  1,   1 },  // GROUP B     Helligkeit LED +
  { 24,  2,   62,  1,   2 },  // GROUP C
  { 25,  2,   63,  1,   3 },  // GROUP D
  { 26,  2,   64,  1,   4 },  // GROUP ALL
  { 27,  2,   65,  1,   5 },  // GROUP ML1   LONG für den MODE Wechsel
  { 28,  2,   66,  1,   6 },  // GROUP ML2
  { 29,  2,   67,  1,   7 },  // GROUP FLOOD
};

struct Gruppe {
  const uint8_t ersteLED;     // erste LED der Gruppe
  const uint8_t letzteLED;    // letzte LED der Gruppe
  char mode;
};
//          A = Einer von der Gruppe   B= alle von Gruppe
Gruppe grp[] {
	{99, 99, 'B'},  // 0: ohne Gruppe
	{ 0, 13, 'A'},  // 1: Gruppe O
	{14, 21, 'B'},  // 2: Gruppe N
	{24, 35, 'B'},  // 3: Gruppe Chaser
	{36, 41, 'A'},  // 4: Gruppe POSITIONS
	{42, 47, 'A'},  // 5: Gruppe M
};

struct PCF_Taste {
  const uint8_t led;          // LED-Nummer 0 bis 47
  const uint8_t grpIDX;       // Gruppenindex
  const uint8_t midiAKanal;   // MIDI A Kanal  NOTE ON auf KANAL1 =1 Kanal2 =2 etc.
  const uint8_t midiANote;    // MIDI A Note   0-127
  const uint8_t midiBKanal;   // MIDI B Kanal
  const uint8_t midiBNote;    // MIDI B Note
};

PCF_Taste pcfTaste[]{
	// PCF EXPANDER A
  //LED\GRP\MIDI NoteA\MIDI NoteB          idx           
  { 255,  0,  2,   5,  1,   0 },  // P00   01 COLOR WIPE 1  a6         MP  S10
  { 255,  0,  2,   4,  1,   1 },  // P01   02 COLOR UV      a5
  { 255,  0,  2,  11,  1,   2 },  // P02   03 COLOR WIPE 2  b6
  { 255,  0,  2,  10,  1,   3 },  // P03   04 COLOR CHASE   b5
  { 255,  0,  2,   3,  1,   4 },  // P04   05 COLOR BLUE    a4
  { 255,  0,  2,   9,  1,   5 },  // P05   06 COLOR PINK    b4
  { 255,  0,  2,   2,  1,   6 },  // P06   07 COLOR GREEN   a3
  { 255,  0,  2,   8,  1,   7 },  // P07   08 COLOR CYAN    b3
  { 255,  0,  2,   1,  1,   8 },  // P10   09 COLOR RED     a2
  { 255,  0,  2,   0,  1,   9 },  // P11   10 COLOR WHITE   a1
  { 255,  0,  2,   7,  1,  10 },  // P12   11 COLOR ORANGE  b2
  { 255,  0,  2,   6,  1,  11 },  // P13   12 COLOR YELLOW  b1
  { 255,  0,  2,  34,  1,  34 },  // P14   13 FLASH 17                  MP  S11
  { 255,  0,  2,  35,  1,  35 },  // P15   14 FLASH 18
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
	// PCF EXPANDER B
  //LED\GRP\MIDI NoteA\MIDI NoteB  
  { 255,  0,  2,  18,  1,  18 },  // P00   16  FLASH 1                   MP S11
  { 255,  0,  2,  19,  1,  19 },  // P01   17  FLASH 2
  { 255,  0,  2,  20,  1,  20 },  // P02   18  FLASH 3
  { 255,  0,  2,  21,  1,  21 },  // P03   19  FLASH 4
  { 255,  0,  2,  22,  1,  22 },  // P04   20  FLASH 5
  { 255,  0,  2,  23,  1,  23 },  // P05   21  FLASH 6
  { 255,  0,  2,  24,  1,  24 },  // P06   22  FLASH 7
  { 255,  0,  2,  25,  1,  25 },  // P07   23  FLASH 8
  { 255,  0,  2,  26,  1,  26 },  // P10   24  FLASH 9
  { 255,  0,  2,  27,  1,  27 },  // P11   25  FLASH 10
  { 255,  0,  2,  28,  1,  28 },  // P12   26  FLASH 11
  { 255,  0,  2,  29,  1,  29 },  // P13   27  FLASH 12
  { 255,  0,  2,  30,  1,  30 },  // P14   28  FLASH 13
  { 255,  0,  2,  31,  1,  31 },  // P15   29  FLASH 14
  { 255,  0,  2,  32,  1,  32 },  // P16   30  FLASH 15
  { 255,  0,  2,  33,  1,  33 },  // P17   31  FLASH 16
	// PCF EXPANDER C                PIN idx   GRP           MODE    LED
  //LED\GRP\MIDI NoteA\MIDI NoteB  
  { 255,  0,  2,  12,  1,  53 },  // P00   32  GOBO OPEN                       MP  S10.13
  { 255,  0,  2,  13,  1,  53 },  // P01   33  GOBO I
  { 255,  0,  2,  14,  1,  53 },  // P02   34  GOBO II
  { 255,  0,  2,  15,  1,  53 },  // P03   35  GOBO III
  { 255,  0,  2,  16,  1,  53 },  // P04   36  GOBO IV
  { 255,  0,  2,  17,  1,  53 },  // P05   37  GOBO V
  { 255,  0,  2,  53,  1,  53 },  // P06   38  GOBO VI
  { 255,  0,  2,  53,  1,  53 },  // P07   39  GOBO Wheel rotation
  {  36,  4,  2,  66,  1,  53 },  // P10   40  POSITIONS I   MODE A  36        MP E2.13
  {  37,  4,  2,  67,  1,  53 },  // P11   41  POSITIONS II  MODE B  37
  {  38,  4,  2,  68,  1,  53 },  // P12   42  POSITIONS III         38
  {  39,  4,  2,  69,  1,  53 },  // P13   43  POSITIONS IV          39
  {  40,  4,  2,  70,  1,  53 },  // P14   44  POSITIONS V           40
  {  41,  4,  2,  71,  1,  53 },  // P15   45  POSITIONS VI          41
  { 255,  0,  2,  98,  1,  53 },  // P16   46  TEMPO TAP                       MP BLACKOUT
  { 255,  0,  2,  53,  1,  53 },  // P17   47  Playlight Reset
	// PCF EXPANDER D                PIN     GRP           MODE    LED
  //LED\GRP\MIDI NoteA\MIDI NoteB 
  {  42,  5,  2,  72,  1,  53 },  // P00   48  M1            MODE A  42  MP  Evironment Chaser
  {  43,  5,  2,  73,  1,  53 },  // P01   49  M2            MODE B  43
  {  44,  5,  2,  74,  1,  53 },  // P02   50  M3                    44
  {  45,  5,  2,  75,  1,  53 },  // P03   51  M4                    45
  {  46,  5,  2,  76,  1,  53 },  // P04   52  M5                    46
  {  47,  5,  2,  77,  1,  53 },  // P05   53  M6                    47
  { 255,  0,  2,  53,  1,  53 },  // P06   54  PRELOAD        PWM 2(MEGA)
  {  22,  0,  2,  35,  1,  53 },  // P07   55  STROBE                22
  {  14,  2,  2,  42,  1,  53 },  // P10   56  N 1           MODE A  14   MP E1.7
  {  15,  2,  2,  43,  1,  53 },  // P11   57  N 2           MODE B  15
  {  16,  2,  2,  44,  1,  53 },  // P12   58  N 3           MODE C  16
  {  17,  2,  2,  45,  1,  53 },  // P13   59  N 4                   17
  {  18,  2,  2,  46,  1,  53 },  // P14   60  N 5                   18
  {  19,  2,  2,  47,  1,  53 },  // P15   61  N 6                   19
  {  20,  2,  2,  48,  1,  53 },  // P16   62  N 7                   20
  {  21,  2,  2,  49,  1,  53 },  // P17   63  N 8                   21
	// PCF EXPANDER E                PIN     GRP           MODE    LED
  //LED\GRP\MIDI NoteA\MIDI NoteB 
  {   0,  1,  2,  54,  1,  53 },  // P00   64  O 1           MODE A   0   MP E2
  {   1,  1,  2,  55,  1,  53 },  // P01   65  O 2           MODE B   1
  {   2,  1,  2,  56,  1,  44 },  // P02   66  O 3           MODE C   2
  {   3,  1,  2,  57,  1,  45 },  // P03   67  O 4                    3
  {   4,  1,  2,  58,  1,  46 },  // P04   68  O 5                    4
  {   5,  1,  2,  59,  1,  47 },  // P05   69  O 6                    5
  {   6,  1,  2,  60,  1,  10 },  // P06   70  O 7                    6
  {   7,  1,  2,  61,  1,  11 },  // P07   71  O 8                    7
  {   8,  1,  2,  62,  1,  27 },  // P10   72  O 9                    8
  {   9,  1,  2,  63,  1,  28 },  // P11   73  O 10                   9
  {  10,  1,  2,  64,  1,  32 },  // P12   74  O 11                  10
  {  11,  1,  2,  65,  1,  27 },  // P13   75  O 12                  11
  {  12,  1,  2,  66,  1,  35 },  // P14   76  O 13                  12
  {  13,  1,  2,  67,  1,  58 },  // P15   77  O 14                  13
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt(78)
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt (79)
	// PCF EXPANDER F                PIN    idx     GRP           MODE    LED
  //LED\GRP\MIDI NoteA\MIDI NoteB 
  {  24,  3,  2,  78,  1,   0 },  // P00   80  Chaser 1      MODE A   24  MP E3
  {  25,  3,  2,  79,  1,   1 },  // P01   81  Chaser 2      MODE B   25
  {  26,  3,  2,  80,  1,   2 },  // P02   82  Chaser 3               26
  {  27,  3,  2,  81,  1,   3 },  // P03   83  Chaser 4               27
  {  28,  3,  2,  82,  1,   4 },  // P04   84  Chaser 5               28
  {  29,  3,  2,  83,  1,   5 },  // P05   85  Chaser 6               29
  {  30,  3,  2,  84,  1,   6 },  // P06   86  Chaser 7               30
  {  31,  3,  2,  85,  1,   7 },  // P07   87  Chaser 8               31
  {  32,  3,  2,  86,  1,   8 },  // P10   88  Chaser 9               32
  {  33,  3,  2,  87,  1,   9 },  // P11   89  Chaser 10              33
  {  34,  3,  2,  88,  1,  10 },  // P12   90  Chaser 11              34
  {  35,  3,  2,  89,  1,  11 },  // P13   91  Chaser 12(OFF)         35
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
};

Die MIDI Ausgabe vom MEGA (PIN22 -29) direkt Funktioniert noch nicht, auch der SerMon. zeigt nichts an wenn ich eine Taste drücke.
Gelötet sind die genau wie die anderen Taster:
PIN an Taster und über 10KOHM zu 5V
und vom Taster die andere Seite an GND.
Muss da noch etwas mit Pullup oder so gemacht werden? oder hab ich da noch einen Bug Anschlußtechnisch?

Das mut dem 2ten Mega bau ich morgen Abend.
So, ich bin dann für heute auch weg

schönen Abend euch allen...

LG Rebecca

:white_check_mark:

Richtig, noch nicht implementiert. Mir ging es nur um den Longpress für den Moduswechsel.

1 Like

Das klappt auch wunderbar :grinning_face:

aktueller experimenteller Zwischenstand
/* Belegung Mega2560
        CIPO = 50   unbenutzt (früher MISO)
DATA  = COPI = 51   Pin von Hardware-SPI (früher MOSI)
CLOCK = SCA  = 52   Pin von Hardware-SPI
LATCH = CS   = 53   Chip Select Pin
OE           = 49   Output Enable, frei wählbarer Pin
*/
/* Usecase by noiasca 2025-11-16 3870/218 in tlc.h */
#define agmue
auto &SerMon = Serial;
auto &MidiA = Serial1;
auto &MidiB = Serial2;
#include <Wire.h>
// EXPANDER (PCF8575)----------------------------------------------------------------------------------------------
constexpr uint8_t inputPins{ 16 };            // Anzahl Pins am PCF8575
constexpr uint8_t kOff{ 143 };                // Kanaloffset
constexpr uint32_t pcfMessIntervall{ 5000 };  // in µs
uint32_t pcfVorhin;

#include "ausgabe.h"
#include "gruppen.h"
#include "tlc.h"
//
uint16_t read16(uint8_t adresse) {
  const uint8_t anzahl = 2;
  Wire.requestFrom(adresse, anzahl);
  uint16_t wert = Wire.read() | (Wire.read() << 8);
  return ~wert;  // Taster schalten nach GND
}
//
class PCF_Board {
  const uint8_t i2cAdd;
  const uint8_t pcfNum;
  uint16_t wertAlt = 0;
public:
  PCF_Board(const uint8_t i2cAdd, const uint8_t pcfNum)
    : i2cAdd(i2cAdd), pcfNum(pcfNum){};

  void init() {
    wertAlt = read16(i2cAdd);
  }
  //
  void auswertung() {
    uint16_t wert = read16(i2cAdd);
    uint16_t diff = wertAlt ^ wert;
    wertAlt = wert;
    if (diff) {
      for (byte j = 0; j < inputPins; j++) {
        if (bitRead(diff, j)) {
          byte idx = j + pcfNum * inputPins;
          if (taste[5].longpress()) {
            if (        (idx == 40) || (idx == 48) || (idx == 80) ) {
                grp[pcfTaste[idx].grpIDX].mode = 'A';
            } else if ( (idx == 41) || (idx == 49) || (idx == 81) ) {
                grp[pcfTaste[idx].grpIDX].mode = 'B';
            } else if ( (idx == 56) || (idx == 64) ) {
                grp[pcfTaste[idx].grpIDX].mode = 'A';
                if (grp[pcfTaste[56].grpIDX].mode == 'C') grp[pcfTaste[56].grpIDX].mode = 'A';
                if (grp[pcfTaste[64].grpIDX].mode == 'C') grp[pcfTaste[64].grpIDX].mode = 'A';
            } else if ( (idx == 57) || (idx == 65) ) {
                grp[pcfTaste[idx].grpIDX].mode = 'B';
                if (grp[pcfTaste[57].grpIDX].mode == 'C') grp[pcfTaste[57].grpIDX].mode = 'B';
                if (grp[pcfTaste[65].grpIDX].mode == 'C') grp[pcfTaste[65].grpIDX].mode = 'B';
            } else if ( (idx == 58) || (idx == 66) ) {
                grp[pcfTaste[58].grpIDX].mode = 'C';
                grp[pcfTaste[66].grpIDX].mode = 'C';
            }
            SerMon.print("longPress idx "); SerMon.print(idx); SerMon.print("\tgrpIDX "); SerMon.print(pcfTaste[idx].grpIDX); SerMon.print("\tMode "); SerMon.println(grp[pcfTaste[idx].grpIDX].mode); 
          } else {
            if (bitRead(wert, j)) {
              printMidiTaste(pcfTaste[idx].midiAKanal, pcfTaste[idx].midiANote, pcfTaste[idx].midiBKanal, pcfTaste[idx].midiBNote, 127);
              outMidiTaste(pcfTaste[idx].midiAKanal, pcfTaste[idx].midiANote, pcfTaste[idx].midiBKanal, pcfTaste[idx].midiBNote, 127);
              SerMon.print("\tMode "); SerMon.println(grp[pcfTaste[idx].grpIDX].mode);
              if (grp[pcfTaste[idx].grpIDX].mode == 'C') {
                for (uint8_t j = grp[1].ersteLED; j <= grp[2].letzteLED; j++) {
                  tlc.off(j);
                }
              } else if (grp[pcfTaste[idx].grpIDX].mode == 'A') {
                for (uint8_t j = grp[pcfTaste[idx].grpIDX].ersteLED; j <= grp[pcfTaste[idx].grpIDX].letzteLED; j++) {
                  tlc.off(j);
                }
              }
              tlc.toggle(pcfTaste[idx].led);
            } else {
              printMidiTaste(pcfTaste[idx].midiAKanal, pcfTaste[idx].midiANote, pcfTaste[idx].midiBKanal, pcfTaste[idx].midiBNote, 0);
              outMidiTaste(pcfTaste[idx].midiAKanal, pcfTaste[idx].midiANote, pcfTaste[idx].midiBKanal, pcfTaste[idx].midiBNote, 0);
              if (grp[pcfTaste[idx].grpIDX].mode == 'D') tlc.off(pcfTaste[idx].led);
            }
          }
        }
      }
      tlc.update();
    }
  }
};
#ifdef agmue
PCF_Board pcfBoard[]{ { 0x3A, 0 }, { 0x3B, 1 }, { 0x3C, 2 }, { 0x20, 3 }, { 0x21, 4 }, { 0x38, 5 } };
#else
PCF_Board pcfBoard[]{ { 0x20, 0 }, { 0x21, 1 }, { 0x22, 2 }, { 0x23, 3 }, { 0x24, 4 }, { 0x25, 5 } };
#endif
//
void loop() {
  for (Taste &t : taste) t.update();
  uint32_t jetzt = micros();

  if (jetzt - pcfVorhin >= pcfMessIntervall) {
    pcfVorhin = jetzt;
    for (PCF_Board &p : pcfBoard) p.auswertung();
  }
}
//
void setup() {
  SerMon.begin(115200);  // Texte und Upload
  MidiA.begin(38400);    // MIDI HairlessMIDI mit 38400, Standard MIDI mit 31250
  MidiB.begin(38400);    // MIDI HairlessMIDI mit 38400, Standard MIDI mit 31250
  delay(1000);

  Wire.begin();
  Wire.setClock(400000);
  for (Taste &t : taste) t.init();
  for (PCF_Board &p : pcfBoard) p.init();

  SPI.begin();
  tlc.start();
  tlc.update();
  pinMode(pinOE, OUTPUT);
  digitalWrite(pinOE, LOW);

  SerMon.println("\nStart ...");
}

gruppen.h:

struct Taste {
  const uint8_t pin;         // Pin am Mega2560
  const uint8_t midiAKanal;  // MIDI A Kanal  NOTE ON auf KANAL1 =1 Kanal2 =2 etc.
  const uint8_t midiANote;   // MIDI A Note   0-127
  const uint8_t midiBKanal;  // MIDI B Kanal
  const uint8_t midiBNote;   // MIDI B Note
  uint32_t vorhin;           // gemerkte Zeit
  bool pressState;           // long == true
  bool aktZustand;
  bool altZustand;
  uint8_t schritt;

  Taste(const uint8_t pin, const uint8_t midiAKanal, const uint8_t midiANote, const uint8_t midiBKanal, const uint8_t midiBNote)
    : pin(pin), midiAKanal(midiAKanal), midiANote(midiANote), midiBKanal(midiBKanal), midiBNote(midiBNote), vorhin(0), pressState(0), aktZustand(0), schritt(0){};

  void init() {
    pinMode(pin, INPUT_PULLUP);
    aktZustand = digitalRead(pin);
    altZustand = aktZustand;
  }

  bool longpress() {
    bool tmp = pressState;
    pressState = false;
    return tmp;
  }

  void update() {
    aktZustand = digitalRead(pin);
    switch (schritt) {
      case 0:
       if ((aktZustand != altZustand) & (!aktZustand)) {
          vorhin = millis();
          pressState = false;
            schritt++;
        }
        break;
      case 1:
        if (millis() - vorhin >= 500) {
          if (aktZustand) {
            schritt = 3;  // short
          } else {
            schritt++;
          }
        }
        break;
      case 2:
        if (aktZustand) {
          schritt = 3;  // short
        } else {
          if (millis() - vorhin >= 1000) {
            pressState = true;
            schritt = 3;
          }
        }
        break;
        case 3:
          if (millis() - vorhin >= 2000) {
            schritt = 0;
          }
        break;
    }
    altZustand = aktZustand;
  }
};

Taste taste[]{
	// Mega2560
  //pin, MIDI NoteA, MIDI NoteB                   
  { 22,  2,   60,  1,   0 },  // GROUP A     Helligkeit LED -   E2.7
  { 23,  2,   61,  1,   1 },  // GROUP B     Helligkeit LED +
  { 24,  2,   62,  1,   2 },  // GROUP C
  { 25,  2,   63,  1,   3 },  // GROUP D
  { 26,  2,   64,  1,   4 },  // GROUP ALL
  { 27,  2,   65,  1,   5 },  // GROUP ML1   LONG für den MODE Wechsel
  { 28,  2,   66,  1,   6 },  // GROUP ML2
  { 29,  2,   67,  1,   7 },  // GROUP FLOOD
};

struct Gruppe {
  const uint8_t ersteLED;     // erste LED der Gruppe
  const uint8_t letzteLED;    // letzte LED der Gruppe
  char mode;
};
//          A = Einer von der Gruppe   B= alle von Gruppe
Gruppe grp[] {
	{99, 99, 'B'},  // 0: ohne Gruppe
	{ 0, 13, 'D'},  // 1: Gruppe O
	{14, 21, 'B'},  // 2: Gruppe N
	{24, 35, 'B'},  // 3: Gruppe Chaser
	{36, 41, 'A'},  // 4: Gruppe POSITIONS
	{42, 47, 'A'},  // 5: Gruppe M
};

struct PCF_Taste {
  const uint8_t led;          // LED-Nummer 0 bis 47
  const uint8_t grpIDX;       // Gruppenindex
  const uint8_t midiAKanal;   // MIDI A Kanal  NOTE ON auf KANAL1 =1 Kanal2 =2 etc.
  const uint8_t midiANote;    // MIDI A Note   0-127
  const uint8_t midiBKanal;   // MIDI B Kanal
  const uint8_t midiBNote;    // MIDI B Note
};

PCF_Taste pcfTaste[]{
	// PCF EXPANDER A
  //LED\GRP\MIDI NoteA\MIDI NoteB          idx           
  { 255,  0,  2,   5,  1,   0 },  // P00   01 COLOR WIPE 1  a6         MP  S10
  { 255,  0,  2,   4,  1,   1 },  // P01   02 COLOR UV      a5
  { 255,  0,  2,  11,  1,   2 },  // P02   03 COLOR WIPE 2  b6
  { 255,  0,  2,  10,  1,   3 },  // P03   04 COLOR CHASE   b5
  { 255,  0,  2,   3,  1,   4 },  // P04   05 COLOR BLUE    a4
  { 255,  0,  2,   9,  1,   5 },  // P05   06 COLOR PINK    b4
  { 255,  0,  2,   2,  1,   6 },  // P06   07 COLOR GREEN   a3
  { 255,  0,  2,   8,  1,   7 },  // P07   08 COLOR CYAN    b3
  { 255,  0,  2,   1,  1,   8 },  // P10   09 COLOR RED     a2
  { 255,  0,  2,   0,  1,   9 },  // P11   10 COLOR WHITE   a1
  { 255,  0,  2,   7,  1,  10 },  // P12   11 COLOR ORANGE  b2
  { 255,  0,  2,   6,  1,  11 },  // P13   12 COLOR YELLOW  b1
  { 255,  0,  2,  34,  1,  34 },  // P14   13 FLASH 17                  MP  S11
  { 255,  0,  2,  35,  1,  35 },  // P15   14 FLASH 18
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
	// PCF EXPANDER B
  //LED\GRP\MIDI NoteA\MIDI NoteB  
  { 255,  0,  2,  18,  1,  18 },  // P00   16  FLASH 1                   MP S11
  { 255,  0,  2,  19,  1,  19 },  // P01   17  FLASH 2
  { 255,  0,  2,  20,  1,  20 },  // P02   18  FLASH 3
  { 255,  0,  2,  21,  1,  21 },  // P03   19  FLASH 4
  { 255,  0,  2,  22,  1,  22 },  // P04   20  FLASH 5
  { 255,  0,  2,  23,  1,  23 },  // P05   21  FLASH 6
  { 255,  0,  2,  24,  1,  24 },  // P06   22  FLASH 7
  { 255,  0,  2,  25,  1,  25 },  // P07   23  FLASH 8
  { 255,  0,  2,  26,  1,  26 },  // P10   24  FLASH 9
  { 255,  0,  2,  27,  1,  27 },  // P11   25  FLASH 10
  { 255,  0,  2,  28,  1,  28 },  // P12   26  FLASH 11
  { 255,  0,  2,  29,  1,  29 },  // P13   27  FLASH 12
  { 255,  0,  2,  30,  1,  30 },  // P14   28  FLASH 13
  { 255,  0,  2,  31,  1,  31 },  // P15   29  FLASH 14
  { 255,  0,  2,  32,  1,  32 },  // P16   30  FLASH 15
  { 255,  0,  2,  33,  1,  33 },  // P17   31  FLASH 16
	// PCF EXPANDER C                PIN idx   GRP           MODE    LED
  //LED\GRP\MIDI NoteA\MIDI NoteB  
  { 255,  0,  2,  12,  1,  53 },  // P00   32  GOBO OPEN                       MP  S10.13
  { 255,  0,  2,  13,  1,  53 },  // P01   33  GOBO I
  { 255,  0,  2,  14,  1,  53 },  // P02   34  GOBO II
  { 255,  0,  2,  15,  1,  53 },  // P03   35  GOBO III
  { 255,  0,  2,  16,  1,  53 },  // P04   36  GOBO IV
  { 255,  0,  2,  17,  1,  53 },  // P05   37  GOBO V
  { 255,  0,  2,  53,  1,  53 },  // P06   38  GOBO VI
  { 255,  0,  2,  53,  1,  53 },  // P07   39  GOBO Wheel rotation
  {  36,  4,  2,  66,  1,  53 },  // P10   40  POSITIONS I   MODE A  36        MP E2.13
  {  37,  4,  2,  67,  1,  53 },  // P11   41  POSITIONS II  MODE B  37
  {  38,  4,  2,  68,  1,  53 },  // P12   42  POSITIONS III         38
  {  39,  4,  2,  69,  1,  53 },  // P13   43  POSITIONS IV          39
  {  40,  4,  2,  70,  1,  53 },  // P14   44  POSITIONS V           40
  {  41,  4,  2,  71,  1,  53 },  // P15   45  POSITIONS VI          41
  { 255,  0,  2,  98,  1,  53 },  // P16   46  TEMPO TAP                       MP BLACKOUT
  { 255,  0,  2,  53,  1,  53 },  // P17   47  Playlight Reset
	// PCF EXPANDER D                PIN     GRP           MODE    LED
  //LED\GRP\MIDI NoteA\MIDI NoteB 
  {  42,  5,  2,  72,  1,  53 },  // P00   48  M1            MODE A  42  MP  Evironment Chaser
  {  43,  5,  2,  73,  1,  53 },  // P01   49  M2            MODE B  43
  {  44,  5,  2,  74,  1,  53 },  // P02   50  M3                    44
  {  45,  5,  2,  75,  1,  53 },  // P03   51  M4                    45
  {  46,  5,  2,  76,  1,  53 },  // P04   52  M5                    46
  {  47,  5,  2,  77,  1,  53 },  // P05   53  M6                    47
  { 255,  0,  2,  53,  1,  53 },  // P06   54  PRELOAD        PWM 2(MEGA)
  {  22,  0,  2,  35,  1,  53 },  // P07   55  STROBE                22
  {  14,  2,  2,  42,  1,  53 },  // P10   56  N 1           MODE A  14   MP E1.7
  {  15,  2,  2,  43,  1,  53 },  // P11   57  N 2           MODE B  15
  {  16,  2,  2,  44,  1,  53 },  // P12   58  N 3           MODE C  16
  {  17,  2,  2,  45,  1,  53 },  // P13   59  N 4                   17
  {  18,  2,  2,  46,  1,  53 },  // P14   60  N 5                   18
  {  19,  2,  2,  47,  1,  53 },  // P15   61  N 6                   19
  {  20,  2,  2,  48,  1,  53 },  // P16   62  N 7                   20
  {  21,  2,  2,  49,  1,  53 },  // P17   63  N 8                   21
	// PCF EXPANDER E                PIN     GRP           MODE    LED
  //LED\GRP\MIDI NoteA\MIDI NoteB 
  {   0,  1,  2,  54,  1,  53 },  // P00   64  O 1           MODE A   0   MP E2
  {   1,  1,  2,  55,  1,  53 },  // P01   65  O 2           MODE B   1
  {   2,  1,  2,  56,  1,  44 },  // P02   66  O 3           MODE C   2
  {   3,  1,  2,  57,  1,  45 },  // P03   67  O 4                    3
  {   4,  1,  2,  58,  1,  46 },  // P04   68  O 5                    4
  {   5,  1,  2,  59,  1,  47 },  // P05   69  O 6                    5
  {   6,  1,  2,  60,  1,  10 },  // P06   70  O 7                    6
  {   7,  1,  2,  61,  1,  11 },  // P07   71  O 8                    7
  {   8,  1,  2,  62,  1,  27 },  // P10   72  O 9                    8
  {   9,  1,  2,  63,  1,  28 },  // P11   73  O 10                   9
  {  10,  1,  2,  64,  1,  32 },  // P12   74  O 11                  10
  {  11,  1,  2,  65,  1,  27 },  // P13   75  O 12                  11
  {  12,  1,  2,  66,  1,  35 },  // P14   76  O 13                  12
  {  13,  1,  2,  67,  1,  58 },  // P15   77  O 14                  13
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt(78)
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt (79)
	// PCF EXPANDER F                PIN    idx     GRP           MODE    LED
  //LED\GRP\MIDI NoteA\MIDI NoteB 
  {  24,  3,  2,  78,  1,   0 },  // P00   80  Chaser 1      MODE A   24  MP E3
  {  25,  3,  2,  79,  1,   1 },  // P01   81  Chaser 2      MODE B   25
  {  26,  3,  2,  80,  1,   2 },  // P02   82  Chaser 3               26
  {  27,  3,  2,  81,  1,   3 },  // P03   83  Chaser 4               27
  {  28,  3,  2,  82,  1,   4 },  // P04   84  Chaser 5               28
  {  29,  3,  2,  83,  1,   5 },  // P05   85  Chaser 6               29
  {  30,  3,  2,  84,  1,   6 },  // P06   86  Chaser 7               30
  {  31,  3,  2,  85,  1,   7 },  // P07   87  Chaser 8               31
  {  32,  3,  2,  86,  1,   8 },  // P10   88  Chaser 9               32
  {  33,  3,  2,  87,  1,   9 },  // P11   89  Chaser 10              33
  {  34,  3,  2,  88,  1,  10 },  // P12   90  Chaser 11              34
  {  35,  3,  2,  89,  1,  11 },  // P13   91  Chaser 12(OFF)         35
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
  { 255,  0,  0,   0,  0,   0 },  // unbenutzt
};

ausgabe.h (neu):

void outMidiTaste(const uint8_t midiAKanal, const uint8_t midiANote, const uint8_t midiBKanal, const uint8_t midiBNote, const byte val) {
  MidiA.write(kOff + midiAKanal);
  MidiA.write(midiANote);
  MidiA.write(val);
  MidiB.write(kOff + midiBKanal);
  MidiB.write(midiBNote);
  MidiB.write(val);
}
//
void printMidiTaste(const uint8_t midiAKanal, const uint8_t midiANote, const uint8_t midiBKanal, const uint8_t midiBNote, const byte val) {
  SerMon.print(kOff + midiAKanal);
  SerMon.print('\t');
  SerMon.print(midiANote);
  SerMon.print('\t');
  SerMon.print(val);
  SerMon.print('\t');
  SerMon.print(kOff + midiBKanal);
  SerMon.print('\t');
  SerMon.print(midiBNote);
  SerMon.print('\t');
  SerMon.println(val);
}

tlc.h (unverändert):

#include <TLC5947_SPI.hpp>

constexpr uint8_t DEVICES{ 2 };    // Anzahl der angeschlossenen Platinen mit je 24 LEDs.
constexpr uint8_t pinLATCH{ 53 };  // Pin für die Datenübernahme.
constexpr uint8_t pinOE{ 49 };     // Pin für die Aktivierung der LED-Treiber (O̲utput E̲nable).

class MyTLC : public TLC5947_SPI {
protected:
  static const uint8_t numPWM = 4;
  const uint16_t valuesPWM[numPWM] = { 50, 200, 1000, 4095 };
  uint8_t stepPWM = numPWM - 3;
  uint16_t offPWM = 0;  // PWM for off state. When you set this value >0 the LED will be slightly dimmed also in off state.

public:
  MyTLC(const uint8_t latch, const uint8_t deviceCount = 1, const uint32_t SPIclock = 8000000UL, SPIClass &spi = SPI)
    : TLC5947_SPI(latch, deviceCount, SPIclock, spi) {}

  // call this function in setup().
  // it can't be called .begin() as there is already a .begin() in the parent class but the parent class has no late binding (not virtual), hence we name it .start()
  void start() {
    begin();  // the parent class needs a .begin()
    setAll(offPWM);
  }

  // switch on
  void on(const uint8_t channel) {
    setPWM(channel, valuesPWM[stepPWM]);
  }

  // switch off
  void off(const uint8_t channel) {
    setPWM(channel, offPWM);
  }

  // toggle
  void toggle(const uint8_t channel) {
    if (channel < (DEVICES * 24)) {
      if (getPWM(channel) != offPWM) {
        off(channel);
      } else {
        on(channel);
      }
    }
  }

  // switch to day lighter mode
  void
  lighterPWM() {
    if (stepPWM < numPWM - 1) {
      stepPWM++;
      modifyActive(valuesPWM[stepPWM]);
    }
  }

  // switch to day darker mode
  void darkerPWM() {
    if (stepPWM > 0) {
      stepPWM--;
      modifyActive(valuesPWM[stepPWM]);
    }
  }

  // change PWM of active channels
  void modifyActive(const uint16_t newPWM) {
    for (byte channel = 0; channel < getChannels(); channel++) {
      if (getPWM(channel) != offPWM) {
        setPWM(channel, newPWM);
      }
    }
  }
};
MyTLC tlc(pinLATCH, DEVICES);  // create an instance of your new class

In welchen Gruppen soll Mode D verwendet werden und wie wird der eingeschaltet?

1 Like

Eine gute Frage.
Es macht Sinn ihn in allen Gruppen zu haben.
Und darum macht es auch Sinn den Mode C zu D zu wandeln, da er ja nur 2 Gruppen betrifft. Das macht es übersichtlicher und passt besser zusammen.

Auswahl dann also statt 3 über die ersten 4 Tasten der Gruppe.
Somit TAUSCHEN C und D die Plätze
Mode A Taste 1 > nur ein von der Gruppe
Mode B Taste 2 > einzeln
Mode C Taste 3 > leichtet nur solange man drückt
Mode D ( nur bei O& N) Taste 4 > nur ein von der Kombinierten Gruppe O und N

Was jetzt noch komfort und eine sichere Bedienung bringen würde:
Wenn long aktiviert :
Blinken alle Möglichkeiten und die aktuelle Auswahl leuchtet.

Derzeit kann ich nur eine Änderung vornehmen, danach wird long beendet. Ist es machbar. Mehrere Änderungen zu machen, solange ich die ML1 Taste drücke und halte?

LG Rebecca

Ja!

Ja, vermutlich einfacher :thinking:

1 Like

Häng mal nur den Mega vom KING an den Rechner und las den seriellen Monitor mitlaufen.
Achte darauf, dass die Ausgabegeschwindigkeit stimmt:

1 Like