LED Tubes DIY, Probleme mit Timing/Synchronität über DMX

Edit:
Lösung:
FastLED.show() blockiert interrupts, sodass DMX nicht mit interrupts arbeiten kann und Arduino keine neuen Daten empfangen kann. Dadurch stockt es.

Guten Tag,

bin beim Bau von zwei LED Tubes (2m länge je Tube) auf Probleme mit der Synchronität gestoßen. Der Code befindet sich am Ende, teilweise auch mit KI geschrieben, da C++ von der Uni schon einige Semester her ist.

Material:

  • Arduino Nano (Aliexpress: Arduino CH340 USB-Treiber 16 MHz ATMEGA328P)
  • DMX Modul (Aliexpress: EGBO MAX485 modul RS485)
  • WS2812B 60LEDs/m
  • Diverse Kabel zum Controller verbinden
  • 24AWG 0.2mm2 Back, 4 Cores um DMX von einer Seite der Tube zur anderen zu schleifen.
  • Knöpfe zur manuellen Bedienung, falls kein DMX Signal anliegt.
  • Eurolite DMX Move Control 512

Alles wird über ein 5V Netzteil versorgt.

Bin eigentlich nur zwei Anleitungen gefolgt:

Aufbau:
LED-Stripe
LED Data -> 470 Ohm Widerstand -> D2 Arduino
LED + -> 5V
LED - -> Masse
Zwischen 5V und Masse ein 1000uF Kondensator

MAX485
VCC -> 5V
GND & DE & RE -> Masse, nur zum Empfang genutzt
RO -> RX-Pin Arduino

DMX Buchse:
Pin 1 -> GND
Pin 2 -> B von MAX485
Pin 3 -> A von MAX485

Arduino Nano Klon
5V Pin -> 5V
RX Pin -> RO von MAX485
D2 -> Data von LED
D4 & D6 & D8 -> Knöpfen
GND -> Knöpfe
GND -> Masse

Der ganze Aufbau dann zweimal, also jede Tube hat ein eigenes Arduino + MAX485 Modul.
Kabel sind eigentlich keinen langen Wege, max ca. 7cm, außer das durchschleifen des DMX Signals von Anfang mit Ende mit dem verdrillten Kabel (2m).

Funktionsweise:
Falls kein DMX anliegt soll der LED Stripe über 3 Knöpfe gesteuert werden:

  1. Knopf = Modus
  2. Knopf = Farbe
  3. Knopf = BPM (je nach Modus einzelne Segmentsteuerung:

Falls ein DMX Signal anliegt, soll es über DMX laufen -> z.B.
Kanal 1 = Modus
Kanal 2 = Farbe
Kanal 3 = BPM

Es gibt 8 Modis, davon sollen die ersten zwei Modis (0 & 1) die Ansteuerung von unterschiedlichen Segmenten ermöglichen z.B. erste Hälfte des LED-Stripes. Dies geschieht hier über die BPM.
Modus 2-6 sind Laufanimationen, hier soll über BPM die Geschwindigkeit festgelegt werden.
Mit den Knöpfen funktioniert auch alles einwandfrei, schnell und zuverlässig.

Wenn der Modus oder die BPM geändert werden, werden die Laufvariablen auf Beginn zurückgesetzt, sodass im Falle von zwei LED Tubes die Animation gleichzeitig beginnt.

Problem:
Nun ist es so, dass die LED Tubes vorallem in Modus 0 & 1 sehr langsam auf das DMX Signal reagieren, sowohl Farbe als auch BPM (Segmentsteuerung). Das heißt wenn ich zwei Tubes über DMX in Reihe verbunden habe und über ein DMX Pult ein gewissen Zustand möchte, dann stellt sich dieser fast immer sehr zeitversetzt ein, auch unterschiedlich schnell in den zwei Tubes.

Bei den Laufanimationen ist das nur selten der Fall, hier funktioniert die BPM & Farbenänderung fast immer perfekt.

Was ich schon probiert habe:

  1. Probleme mit DMX Werten nah an einem anderen Bereich -> Werte mittig genommen -> hat das Problem nicht gelöst.
  2. Show im DMX Controller programmiert, sodass der DMX Wert nicht stetig steigt, sondern ziemlich instant auf ein Wert springt um den Übergang zu vermeiden -> hat nur minimal etwas geholfen.
  3. Im Code einen "Delay" eingebaut, sodass der Modus erst geändert wird, wenn der DMX Wert eine Zeit X stabil bleibt -> hat nichts gebracht.
  4. Meiner Recherche nach ist ja sowohl das Arduino, als auch DMX schnell genug, sodass quasi keine extrem sichtbaren Synchronitätsunterschiede da sein sollten.
  5. Tubes abgewechselt, also einmal Tube1 als erstes in der Reihenfolge, einmal Tube2 als erstes -> kein Unterschied.

Nun zu der eigentlich Frage:
Hat jemand schonmal solche Probleme gehabt oder hat eventuell eine Möglichkeit, woran es noch liegen könnte?

Was mich persönlich am meisten verwirrt ist, dass mit den Knöpfen alles einwandfrei funktioniert und mit DMX die Laufanimationen ziemlich gut funktionieren, aber nicht die einzelne Segmentansteuerung (mode 0 & 1)

#include <FastLED.h>
#include <DMXSerial.h>

#define LED_PIN     2
#define NUM_LEDS    118
#define BTN_MODE    4  // Taster für Moduswechsel
#define BTN_COLOR   6  // Taster für Farbwechsel
#define BTN_SPEED   8  // Taster für Geschwindigkeitswechsel

CRGB leds[NUM_LEDS];

// DMX-Kanäle
#define dmxMode   14
#define dmxColor  15
#define dmxBPM    16

//Standartwerte
int mode = 0;
int colorMode = 0;
int BPM = 95;

int lastMode = -1;
int lastBPM = 95;

byte hue = 0;
unsigned long interval = 60000 / (BPM * NUM_LEDS);  // Startwert für das Intervall
unsigned long lastModeChange = 0;
unsigned long lastColorChange = 0;
unsigned long lastSpeedChange = 0;
unsigned long debounceDelay = 350;
unsigned long currentMillis = 0;

//DMX Werte
int dmxModeVal = 0;
int dmxColorVal = 0;
int dmxBPMVal = 0;

//Globale Variablen für Modis
      bool firstHalfOn = true;  // Variable, um festzustellen, welche Hälfte gerade an ist
      unsigned long lastHalfChange = 0;  // Letztes Wechselzeitpunkt der Hälften

      unsigned long lastUpdateTime = 0;
      int position1 = 0;  // Startposition für das erste Segment

      int position2 = NUM_LEDS - 1;  // Startposition am Ende des Stripes

      int positionLeft1 = 0;  // Startposition von links
      int positionRight1 = NUM_LEDS - 1;  // Startposition von rechts

      int positionLeft2 = NUM_LEDS / 2;  // Startposition von der Mitte links
      int positionRight2 = NUM_LEDS / 2;  // Startposition von der Mitte rechts

// Farben-Array für den Regenbogen (einschließlich Weiß am Anfang)
CRGB colors[] = {
  CRGB::White,           // Weiß
  CRGB(255, 0, 0),       // Rot
  CRGB(255, 110, 0),     // Orange
  CRGB(255, 255, 0),     // Gelb
  CRGB(0, 255, 0),       // Grün
  CRGB(70, 130, 180),    // Stahlblau (Steel Blue)
  CRGB(0, 0, 255),       // Blau
  CRGB(75, 0, 130),      // Indigo
  CRGB(255, 20, 147)     // Pink
};

void setup() {
  // FastLED Setup
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(200);

  // DMX Setup
  DMXSerial.init(DMXReceiver); // Aktiviert den DMX-Receiver

  // Pin Setup
  pinMode(BTN_MODE, INPUT_PULLUP);   // Taster für Moduswechsel
  pinMode(BTN_COLOR, INPUT_PULLUP);  // Taster für Farbwechsel
  pinMode(BTN_SPEED, INPUT_PULLUP);  // Taster für Geschwindigkeitswechsel bzw. Segmentwechsel (mode 0 u 1)
}

void resetModeState() {
  // Modus 3
  firstHalfOn = true;
  lastHalfChange = 0;
  // Modus 4
  lastUpdateTime = 0;
  position1 = 0;
  // Modus 5
  position2 = NUM_LEDS - 1;
  // Modus 6
  positionLeft1 = 0;
  positionRight1 = NUM_LEDS - 1;
  // Modus 7
  positionLeft2 = NUM_LEDS / 2;
  positionRight2 = NUM_LEDS / 2;
  // Reset Hue für Modus 2
  hue = 0;
}

void loop() {
  currentMillis = millis();

  // DMX-Daten
  dmxModeVal = DMXSerial.read(dmxMode);
  dmxColorVal = DMXSerial.read(dmxColor);
  dmxBPMVal = DMXSerial.read(dmxBPM);

  // Modesteuerung
  if (dmxModeVal != 0) {
    mode = map(dmxModeVal, 0, 255, 0, 7);

    if (mode != lastMode) {
      resetModeState();
      lastMode = mode;
    }
  } 
  else {

    if (digitalRead(BTN_MODE) == LOW && (millis() - lastModeChange) > debounceDelay) {
      mode = (mode + 1) % 8;
      if (mode != lastMode) {
        resetModeState();
        lastMode = mode;
      }
      lastModeChange = millis();
    }
  }

  // Farbsteuerung
  if (dmxColorVal != 0) {
    colorMode = map(dmxColorVal, 0, 255, 0, 8);
  } 
  else {
    if (digitalRead(BTN_COLOR) == LOW && (millis() - lastColorChange) > debounceDelay) {
      colorMode = (colorMode + 1) % 9;
      lastColorChange = millis();
    }
  }
  
  // BPM / Segmentsteuerung
  if (dmxBPMVal != 0) {
    switch (dmxBPMVal / 32) {
      case 0:  BPM = 95; break;
      case 1:  BPM = 110; break;
      case 2:  BPM = 125; break;
      case 3:  BPM = 140; break;
      case 4:  BPM = 155; break;
      case 5:  BPM = 170; break;
      case 6:  BPM = 185; break;
      case 7:  BPM = 200; break;
    }
    if (BPM != lastBPM) {
      resetModeState();
      lastBPM = BPM;
    }
  }
  else {
    if (digitalRead(BTN_SPEED) == LOW && (millis() - lastSpeedChange) > debounceDelay) {
      BPM = (BPM + 15 > 200) ? 95 : BPM + 15;
      lastSpeedChange = millis();
      if (BPM != lastBPM) {
        resetModeState();
        lastBPM = BPM;
      }
    }
  }

  // Aktualisiere das Intervall mit der neuen BPM
  interval = 60000 / (BPM * NUM_LEDS);


  // Modi im Switch-Case-Block behandeln
  switch (mode) {
    case 0: {
      // Zuerst alle LEDs schwarz setzen
      fill_solid(leds, NUM_LEDS, CRGB::Black);

      switch (BPM) {
        case 95: {
          // Alle LEDs in der gewählten Farbe
          fill_solid(leds, NUM_LEDS, colors[colorMode]);
          FastLED.show();
          break;
        }
        case 110: {
          // Erstes Drittel an
          int thirdLength = NUM_LEDS / 3;
          for (int i = 0; i < thirdLength; i++) {
            leds[i] = colors[colorMode];
          }
          FastLED.show();
          break;
        }
        case 125: {
          // Zweites Drittel an
          int thirdLength = NUM_LEDS / 3;
          for (int i = thirdLength; i < 2 * thirdLength; i++) {
            leds[i] = colors[colorMode];
          }
          FastLED.show();
          break;
        }
        case 140: {
          // Drittes Drittel an
          int thirdLength = NUM_LEDS / 3;
          for (int i = 2 * thirdLength; i < NUM_LEDS; i++) {
            leds[i] = colors[colorMode];
          }
          FastLED.show();
          break;
        }
        case 155: {
          // Erstes und drittes Viertel an
          int quarterLength = NUM_LEDS / 4;
          for (int i = 0; i < quarterLength; i++) {
            leds[i] = colors[colorMode];
          }
          for (int i = 2 * quarterLength; i < 3 * quarterLength; i++) {
            leds[i] = colors[colorMode];
          }
          FastLED.show();
          break;
        }
        case 170: {
          // Zweites und viertes Viertel an
          int quarterLength = NUM_LEDS / 4;
          for (int i = quarterLength; i < 2 * quarterLength; i++) {
            leds[i] = colors[colorMode];
          }
          for (int i = 3 * quarterLength; i < NUM_LEDS; i++) {
            leds[i] = colors[colorMode];
          }
          FastLED.show();
          break;
        }
        case 185: {
          // Jedes 1., 3., 5. Segment an
          const int segments = 10;
          const int segmentLength = NUM_LEDS / segments;
          for (int i = 0; i < segments; i++) {
            if (i % 2 == 0) {
              int startIdx = i * segmentLength;
              int endIdx = (i + 1) * segmentLength - 1;
              if (i == segments - 1) {
                endIdx = NUM_LEDS - 1;
                for (int j = startIdx; j <= endIdx; j++) {
                  leds[j] = colors[colorMode];
                }
              }
            }
            if (NUM_LEDS % segments != 0) {
              int remainingStartIdx = segments * segmentLength;
              for (int j = remainingStartIdx; j < NUM_LEDS; j++) {
                leds[j] = colors[colorMode];
              }
            }
          }
          FastLED.show();
          break;
        }
        case 200: {
          // Jedes 2., 4., 6. Segment an
          const int segments = 10;
          const int segmentLength = NUM_LEDS / segments;
          for (int i = 0; i < segments; i++) {
            if (i % 2 != 0) {
              int startIdx = i * segmentLength;
              int endIdx = (i + 1) * segmentLength - 1;
              for (int j = startIdx; j <= endIdx; j++) {
                leds[j] = colors[colorMode];
              }
            }
          }
          FastLED.show();
          break;
        }
      }
      break;
    }
    case 1: {
      // Zuerst alle LEDs schwarz setzen
      fill_solid(leds, NUM_LEDS, CRGB::Black);

      switch (BPM) {
        case 95: {  // Effekt 1: Alle LEDs aus
          FastLED.show();
          break;
        }
        case 110: {  // Effekt 2: Erste Hälfte an
          int halfLength = NUM_LEDS / 2;
          for (int i = 0; i < halfLength; i++) {
            leds[i] = colors[colorMode];
          }
          FastLED.show();
          break;
        }
        case 125: {  // Effekt 3: Zweite Hälfte an
          int halfLength = NUM_LEDS / 2;
          for (int i = halfLength; i < NUM_LEDS; i++) {
            leds[i] = colors[colorMode];
          }
          FastLED.show();
          break;
        }
        case 140: {  // Effekt 7: Erstes Fünftel an
          int fifthLength = NUM_LEDS / 5;
          for (int i = 0; i < fifthLength; i++) {
            leds[i] = colors[colorMode];
          }
          FastLED.show();
          break;
        }
        case 155: {  // Effekt 8: Zweites Fünftel an
          int fifthLength = NUM_LEDS / 5;
          for (int i = fifthLength; i < 2 * fifthLength; i++) {
            leds[i] = colors[colorMode];
          }
          FastLED.show();
          break;
        }
        case 170: {  // Effekt 9: Drittes Fünftel an
          int fifthLength = NUM_LEDS / 5;
          for (int i = 2 * fifthLength; i < 3 * fifthLength; i++) {
            leds[i] = colors[colorMode];
          }
          FastLED.show();
          break;
        }
        case 185: {  // Effekt 10: Viertes Fünftel an
          int fifthLength = NUM_LEDS / 5;
          for (int i = 3 * fifthLength; i < 4 * fifthLength; i++) {
            leds[i] = colors[colorMode];
          }
          FastLED.show();
          break;
        }
        case 200: {  // Effekt 11: Fünftes Fünftel an
          int fifthLength = NUM_LEDS / 5;
          for (int i = 4 * fifthLength; i < NUM_LEDS; i++) {
            leds[i] = colors[colorMode];
          }
          FastLED.show();
          break;
        }
      }
      break;
    }
    case 2: {
      static unsigned long lastHueUpdate = 0;  // Speichert den letzten Update-Zeitpunkt
      unsigned long currentInterval = interval * 5;  // Intervall basierend auf BPM

      if (millis() - lastHueUpdate > currentInterval) {
        fill_solid(leds, NUM_LEDS, CHSV(hue, 255, 255));
        FastLED.show();
    
        hue++;
        if (hue >= 255) {
          hue = 0;
        }
    
        lastHueUpdate = millis();  // Aktualisiere den Timer
      }
      break;
    }
    case 3: {
      // Modus 2: Abwechselnd die erste Hälfte an und die zweite Hälfte, abhängig von BPM
      currentMillis = millis();
      interval = 60000 / BPM;

      if (currentMillis - lastHalfChange >= interval) {
        if (firstHalfOn) {
          // Erste Hälfte an, zweite Hälfte aus
          for (int i = 0; i < NUM_LEDS / 2; i++) {
            leds[i] = colors[colorMode];  // Erste Hälfte in der gewählten Farbe
          }
          for (int i = NUM_LEDS / 2; i < NUM_LEDS; i++) {
            leds[i] = CRGB::Black;  // Zweite Hälfte aus
          }
        } 
        else {
          // Zweite Hälfte an, erste Hälfte aus
          for (int i = 0; i < NUM_LEDS / 2; i++) {
            leds[i] = CRGB::Black;  // Erste Hälfte aus
          }
          for (int i = NUM_LEDS / 2; i < NUM_LEDS; i++) {
            leds[i] = colors[colorMode];  // Zweite Hälfte in der gewählten Farbe
          }
        }
        FastLED.show();  // LEDs aktualisieren
        firstHalfOn = !firstHalfOn;  // Wechseln zwischen den Hälften
        lastHalfChange = currentMillis;  // Speichere das aktuelle Zeitstempel
      }
      break;  
    }
    case 4: {
      // Mode 3: Lauflicht mit immer 5 leuchtenden LEDs (verschiebt sich schrittweise)
      const int trailLength = 15;  // Immer 5 LEDs gleichzeitig an
      const int stepLength = 5;   // Verschiebung der LEDs um jeweils 5

      // Berechne das Intervall für Mode 3 (extra Intervall nur für Mode 3)
      unsigned long intervalMode3 = 60000 / (BPM * (NUM_LEDS / stepLength));  // Berechne das Intervall für Mode 3

      if (millis() - lastUpdateTime > intervalMode3) {
        fill_solid(leds, NUM_LEDS, CRGB::Black);  // Alle LEDs ausschalten

        // Setze 5 LEDs gleichzeitig an, beginnend mit der aktuellen Position
        for (int i = 0; i < trailLength; i++) {
            int ledPos = position1 + i;
            if (ledPos < NUM_LEDS) {
                leds[ledPos] = colors[colorMode];  // Färbe die LED in der gewählten Farbe
            }
        }

        FastLED.show();
        position1 += stepLength;  // Verschiebe die Position um 5 LEDs

        // Wenn das Ende des Stripes erreicht ist, beginne wieder am Anfang
        if (position1 >= NUM_LEDS) {
            position1 = 0;  // Zurücksetzen auf den Anfang
        }

        lastUpdateTime = millis();  // Speichere die aktuelle Zeit
      }
      break;
    }
    case 5: {
      // Mode 4: Rückwärts Lauflicht (beginnend vom Ende)
      const int trailLength = 15;  // Immer 5 LEDs gleichzeitig an
      const int stepLength = 5;   // Verschiebung der LEDs um jeweils 5

      // Berechne das Intervall für Mode 4 (extra Intervall nur für Mode 4)
      unsigned long intervalMode4 = 60000 / (BPM * (NUM_LEDS / stepLength));  // Berechne das Intervall für Mode 4

      if (millis() - lastUpdateTime > intervalMode4) {
        fill_solid(leds, NUM_LEDS, CRGB::Black);  // Alle LEDs ausschalten

        // Setze 5 LEDs gleichzeitig an, beginnend mit der aktuellen Position
        for (int i = 0; i < trailLength; i++) {
          int ledPos = position2 - i;
          if (ledPos >= 0) {
            leds[ledPos] = colors[colorMode];  // Färbe die LED in der gewählten Farbe
          }
        }

        FastLED.show();
        position2 -= stepLength;  // Verschiebe die Position um 5 LEDs rückwärts

        // Wenn das Ende des Stripes erreicht ist, beginne wieder am Ende
        if (position2 < 0) {
          position2 = NUM_LEDS - 1;  // Zurücksetzen auf das Ende
        }

        lastUpdateTime = millis();  // Speichere die aktuelle Zeit
      }
      break;
    }
    case 6: {
      // Mode 5: Zwei Lauflichter, die von beiden Seiten zur Mitte laufen und verschwinden, aber immer wieder von vorne starten
      const int trailLength = 10;  // Immer 15 LEDs gleichzeitig an
      const int stepLength = 5;   // Verschiebung der LEDs um jeweils 5

      // Berechne das Intervall für Mode 5
      unsigned long intervalMode5 = 60000 / (BPM * (NUM_LEDS / (stepLength*2)));  // Berechne das Intervall für Mode 5

      if (millis() - lastUpdateTime > intervalMode5) {
        fill_solid(leds, NUM_LEDS, CRGB::Black);  // Alle LEDs ausschalten

        // Setze 15 LEDs gleichzeitig an, beginnend von der linken Seite
        for (int i = 0; i < trailLength; i++) {
          int ledPos = positionLeft1 + i;
          if (ledPos < NUM_LEDS) {
            leds[ledPos] = colors[colorMode];  // Färbe die LED in der gewählten Farbe
          }
        }

        // Setze 15 LEDs gleichzeitig an, beginnend von der rechten Seite
        for (int i = 0; i < trailLength; i++) {
          int ledPos = positionRight1 - i;
          if (ledPos >= 0) {
            leds[ledPos] = colors[colorMode];  // Färbe die LED in der gewählten Farbe
          }
        }

        // Berechne den Abstand zwischen den beiden Positionen (soll nach und nach kleiner werden)
         int distance = positionRight1 - positionLeft1;

        // Verschiebe die Positionen langsamer zusammen, sodass sich die LEDs mehr in der Mitte treffen
       if (distance > trailLength) {
        positionLeft1 += stepLength;  // Verschiebe die Position nach rechts
        positionRight1 -= stepLength;  // Verschiebe die Position nach links
      }

      // Wenn die Mitte erreicht ist, beginne wieder von vorne
      if (distance <= trailLength) {
        // Setze die Positionen zurück, um den Effekt neu zu starten
        positionLeft1 = 0;
        positionRight1 = NUM_LEDS - 1;
      }

      FastLED.show();
      lastUpdateTime = millis();  // Speichere die aktuelle Zeit
      }
      break;
    }
    case 7: {
      // Mode 6: Zwei Lauflichter, die von der Mitte nach außen laufen und immer wieder von vorne starten
      const int trailLength = 10;  // Immer 5 LEDs gleichzeitig an
      const int stepLength = 5;   // Verschiebung der LEDs um jeweils 5

      // Berechne das Intervall für Mode 6
      unsigned long intervalMode6 = 60000 / (BPM * (NUM_LEDS / (stepLength * 2)));  // Berechne das Intervall für Mode 6

      if (millis() - lastUpdateTime > intervalMode6) {
        fill_solid(leds, NUM_LEDS, CRGB::Black);  // Alle LEDs ausschalten

        // Setze 5 LEDs gleichzeitig an, beginnend von der linken Mitte
        for (int i = 0; i < trailLength; i++) {
          int ledPos = positionLeft2 - i;
          if (ledPos >= 0) {
            leds[ledPos] = colors[colorMode];  // Färbe die LED in der gewählten Farbe
          }
        }

        // Setze 5 LEDs gleichzeitig an, beginnend von der rechten Mitte
        for (int i = 0; i < trailLength; i++) {
          int ledPos = positionRight2 + i;
          if (ledPos < NUM_LEDS) {
            leds[ledPos] = colors[colorMode];  // Färbe die LED in der gewählten Farbe
          }
        }

        // Verschiebe die Positionen nach außen, sodass sich die LEDs in beide Richtungen ausbreiten
        if (positionLeft2 > 0 && positionRight2 < NUM_LEDS - 1) {
          positionLeft2 -= stepLength;  // Verschiebe die Position nach links
          positionRight2 += stepLength;  // Verschiebe die Position nach rechts
        }

        // Wenn beide Positionen das Ende erreichen, beginne wieder von der Mitte
        if (positionLeft2 <= 0 || positionRight2 >= NUM_LEDS - 1) {
          // Setze die Positionen zurück, um den Effekt neu zu starten
          positionLeft2 = NUM_LEDS / 2;
          positionRight2 = NUM_LEDS / 2;
        }

        FastLED.show();
        lastUpdateTime = millis();  // Speichere die aktuelle Zeit
      }
    }
    break;
  }
}

Um es vielleicht anschaulicher darzustellen im folgenden ein Video, falls das hier erlaubt ist:
In der ersten Hälfte werden einzelne Segmente angesteuert (also Modus 0 & 1). In der zweiten Hälfte dann unterschiedliche Laufanimationen. Alles über eine voreingestellte Show im Eurolite DMX Controller

Code von @my_xy_projekt mit den Änderungen, dass es in Mode 0 & 1 auch beim Farbwechsel aktualisiert:

#define FASTLED_ALLOW_INTERRUPTS 1
#define FASTLED_INTERRUPT_RETRY_COUNT 2

#include <FastLED.h>
#include <DMXSerial.h>

#define LED_PIN     2
#define NUM_LEDS    118
#define BTN_MODE    4  // Taster für Moduswechsel
#define BTN_COLOR   6  // Taster für Farbwechsel
#define BTN_SPEED   8  // Taster für Geschwindigkeitswechsel



CRGB leds[NUM_LEDS];

// DMX-Kanäle

#define dmxMode   14
#define dmxColor  15
#define dmxBPM    16

constexpr uint8_t halfLength {NUM_LEDS / 2};
constexpr uint8_t thirdLength {NUM_LEDS / 3};
constexpr uint8_t quarterLength {NUM_LEDS / 4};
constexpr uint8_t fifthLength {NUM_LEDS / 5};

//Standartwerte
int mode = 0;
int colorMode = 0;
int BPM = 95;

int lastMode = -1;
int lastBPM = 95;

byte hue = 0;
unsigned long interval = 60000 / (BPM * NUM_LEDS);  // Startwert für das Intervall
unsigned long lastModeChange = 0;
unsigned long lastColorChange = 0;
unsigned long lastSpeedChange = 0;
unsigned long debounceDelay = 350;
unsigned long currentMillis = 0;

//DMX Werte
int dmxModeVal = 0;
int dmxColorVal = 0;
int dmxBPMVal = 0;

//Globale Variablen für Modis
bool firstHalfOn = true;  // Variable, um festzustellen, welche Hälfte gerade an ist
unsigned long lastHalfChange = 0;  // Letztes Wechselzeitpunkt der Hälften

unsigned long lastUpdateTime = 0;
int position1 = 0;  // Startposition für das erste Segment

int position2 = NUM_LEDS - 1;  // Startposition am Ende des Stripes

int positionLeft1 = 0;  // Startposition von links
int positionRight1 = NUM_LEDS - 1;  // Startposition von rechts

int positionLeft2 = halfLength;  // Startposition von der Mitte links
int positionRight2 = halfLength;  // Startposition von der Mitte rechts

// Farben-Array für den Regenbogen (einschließlich Weiß am Anfang)
CRGB colors[] =
{
  CRGB::White,           // Weiß
  CRGB(255, 0, 0),       // Rot
  CRGB(255, 110, 0),     // Orange
  CRGB(255, 255, 0),     // Gelb
  CRGB(0, 255, 0),       // Grün
  CRGB(70, 130, 180),    // Stahlblau (Steel Blue)
  CRGB(0, 0, 255),       // Blau
  CRGB(75, 0, 130),      // Indigo
  CRGB(255, 20, 147)     // Pink
};

void setup()
{
  // FastLED Setup
  FastLED.addLeds < WS2812B, LED_PIN, GRB > (leds, NUM_LEDS);
  FastLED.setBrightness(200);
  // *** Neu: setze eine Max-Refresh-Rate für kleinere Show-Blöcke ***
  FastLED.setMaxRefreshRate(400);  // max. 400 Updates pro Sekunde → ~2.5 ms Blöcke
  // DMX Setup
  DMXSerial.init(DMXReceiver); // Aktiviert den DMX-Receiver
  // Pin Setup
  pinMode(BTN_MODE, INPUT_PULLUP);   // Taster für Moduswechsel
  pinMode(BTN_COLOR, INPUT_PULLUP);  // Taster für Farbwechsel
  pinMode(BTN_SPEED, INPUT_PULLUP);  // Taster für Geschwindigkeitswechsel bzw. Segmentwechsel (mode 0 u 1)
}

void resetModeState()
{
  // Modus 3
  firstHalfOn = true;
  lastHalfChange = 0;
  // Modus 4
  lastUpdateTime = 0;
  position1 = 0;
  // Modus 5
  position2 = NUM_LEDS - 1;
  // Modus 6
  positionLeft1 = 0;
  positionRight1 = NUM_LEDS - 1;
  // Modus 7
  positionLeft2 = halfLength;
  positionRight2 = halfLength;
  // Reset Hue für Modus 2
  hue = 0;
}

void dmxModeControl()
{
  // Modesteuerung
  if (dmxModeVal != 0)
  { mode = map(dmxModeVal, 0, 255, 0, 7); }
  else if (currentMillis - lastModeChange > debounceDelay)
  {
    if (digitalRead(BTN_MODE) == LOW)
    {
      mode = (mode + 1) % 8;
      lastModeChange = currentMillis;
    }
  }

  if (mode != lastMode)
  {
    resetModeState();
    lastMode = mode;
  }
}

void dmxColorControl()
{
  // Farbsteuerung
  if (dmxColorVal != 0)
  { colorMode = map(dmxColorVal, 0, 255, 0, 8); }
  else if (currentMillis - lastColorChange > debounceDelay)
  {
    if (digitalRead(BTN_COLOR) == LOW)
    {
      colorMode = (colorMode + 1) % 9;
      lastColorChange = currentMillis;
    }
  }
}

void dmxBPMControl()
{
  // BPM / Segmentsteuerung
  if (dmxBPMVal != 0) {
    switch (dmxBPMVal / 32) {
      case 0: BPM = 95; break;    // 0–31
      case 1: BPM = 110; break;   // 32–63
      case 2: BPM = 125; break;   // 64–95
      case 3: BPM = 140; break;   // 96–127
      case 4: BPM = 155; break;   // 128–159
      case 5: BPM = 170; break;   // 160–191
      case 6: BPM = 185; break;   // 192–223
      case 7: BPM = 200; break;   // 224–255
    }
  }
  else if (currentMillis - lastSpeedChange > debounceDelay)
  {
    if (digitalRead(BTN_SPEED) == LOW)
    {
      BPM = (BPM + 15 > 200) ? 95 : BPM + 15;
      lastSpeedChange = currentMillis;
    }

    if (BPM != lastBPM)
    {
      resetModeState();
      lastBPM = BPM;
    }
  }
  if (BPM != lastBPM)
  {
    resetModeState();
    lastBPM = BPM;
  }
}

void loop()
{
  currentMillis = millis();
  // DMX-Daten
  dmxModeVal = DMXSerial.read(dmxMode);
  dmxColorVal = DMXSerial.read(dmxColor);
  dmxBPMVal = DMXSerial.read(dmxBPM);
  dmxModeControl();
  dmxColorControl();
  dmxBPMControl();
  setLedStripe();
}

void setModeNullBPM()
{
  const int segments = 10;
  const int segmentLength = NUM_LEDS / segments;

  switch (BPM)
  {
    case 95:
      // Alle LEDs in der gewählten Farbe
      fill_solid(leds, NUM_LEDS, colors[colorMode]);
      break;

    case 110:

      // Erstes Drittel an
      for (int i = 0; i < thirdLength; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 125:

      // Zweites Drittel an
      for (int i = thirdLength; i < 2 * thirdLength; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 140:

      // Drittes Drittel an
      for (int i = 2 * thirdLength; i < NUM_LEDS; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 155:

      // Erstes und drittes Viertel an
      for (int i = 0; i < quarterLength; i++)
      { leds[i] = colors[colorMode]; }

      for (int i = 2 * quarterLength; i < 3 * quarterLength; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 170:

      // Zweites und viertes Viertel an
      for (int i = quarterLength; i < 2 * quarterLength; i++)
      { leds[i] = colors[colorMode]; }

      for (int i = 3 * quarterLength; i < NUM_LEDS; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 185:

      // Jedes 1., 3., 5. Segment an
      for (int i = 0; i < segments; i++)
      {
        if (i % 2 == 0)  // Jedes 1., 3., 5. Segment (Index 0, 2, 4, ...)
        {
          int startIdx = i * segmentLength;
          int endIdx = (i + 1) * segmentLength - 1;

          // Sicherstellen, dass wir bei ungerader LED-Zahl das letzte Segment korrekt behandeln
          if (i == segments - 1)
          {
            endIdx = NUM_LEDS - 1;
          }

          for (int j = startIdx; j <= endIdx; j++)
          {
            leds[j] = colors[colorMode];
          }
        }
      }

  // Optional: Restliche LEDs außerhalb gleichmäßig verteilter Segmente füllen
  if (NUM_LEDS % segments != 0)
  {
    int remainingStartIdx = segments * segmentLength;
    for (int j = remainingStartIdx; j < NUM_LEDS; j++)
    {
      leds[j] = colors[colorMode];
    }
  }

  break;

    case 200:

      // Jedes 2., 4., 6. Segment an
      for (int i = 0; i < segments; i++)
      {
        if (i % 2 != 0)
        {
          int startIdx = i * segmentLength;
          int endIdx = (i + 1) * segmentLength - 1;

          for (int j = startIdx; j <= endIdx; j++)
          { leds[j] = colors[colorMode]; }
        }
      }

      break;
  }

  FastLED.show();
}

void setModeOneBPM()
{
  switch (BPM)
  {
    case 95:    // Effekt 1: Alle LEDs aus
      break;

    case 110:    // Effekt 2: Erste Hälfte an
      for (int i = 0; i < halfLength; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 125:    // Effekt 3: Zweite Hälfte an
      for (int i = halfLength; i < NUM_LEDS; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 140:    // Effekt 7: Erstes Fünftel an
      for (int i = 0; i < fifthLength; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 155:    // Effekt 8: Zweites Fünftel an
      for (int i = fifthLength; i < 2 * fifthLength; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 170:    // Effekt 9: Drittes Fünftel an
      for (int i = 2 * fifthLength; i < 3 * fifthLength; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 185:    // Effekt 10: Viertes Fünftel an
      for (int i = 3 * fifthLength; i < 4 * fifthLength; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 200:    // Effekt 11: Fünftes Fünftel an
      for (int i = 4 * fifthLength; i < NUM_LEDS; i++)
      { leds[i] = colors[colorMode]; }

      break;
  }

  FastLED.show();
}

void setModeTwoBPM()
{
  static unsigned long lastHueUpdate = 0;
  static uint8_t lastMode = 255;
  
  // Reset timing when entering mode 2
  if(mode != lastMode) {
    lastHueUpdate = currentMillis;
    lastMode = mode;
  }

  // Neuberechnung des Intervalls bei jedem Aufruf
  unsigned long currentInterval = 60000 / (BPM * 5);  // Direkte BPM-Berechnung

  if (currentMillis - lastHueUpdate > currentInterval)
  {
    fill_solid(leds, NUM_LEDS, CHSV(hue, 255, 255));
    hue++;
    lastHueUpdate = currentMillis;
    FastLED.show();
  }
}

void setModeThreeBPM()
{
  // Modus 2: Abwechselnd die erste Hälfte an und die zweite Hälfte, abhängig von BPM
  interval = 60000 / BPM;

  if (currentMillis - lastHalfChange >= interval)
  {
    if (firstHalfOn)
    {
      // Erste Hälfte an, zweite Hälfte aus
      for (int i = 0; i < halfLength; i++)
      { leds[i] = colors[colorMode]; }  // Erste Hälfte in der gewählten Farbe

      for (int i = halfLength; i < NUM_LEDS; i++)
      { leds[i] = CRGB::Black; }  // Zweite Hälfte aus
    }
    else
    {
      // Zweite Hälfte an, erste Hälfte aus
      for (int i = 0; i < halfLength; i++)
      { leds[i] = CRGB::Black; }  // Erste Hälfte aus

      for (int i = halfLength; i < NUM_LEDS; i++)
      { leds[i] = colors[colorMode]; }  // Zweite Hälfte in der gewählten Farbe
    }

    FastLED.show();  // LEDs aktualisieren
    firstHalfOn = !firstHalfOn;  // Wechseln zwischen den Hälften
    lastHalfChange = currentMillis;  // Speichere das aktuelle Zeitstempel
  }
}

void setModeFourBPM()
{
  // Mode 3: Lauflicht mit immer 5 leuchtenden LEDs (verschiebt sich schrittweise)
  const int trailLength = 15;  // Immer 5 LEDs gleichzeitig an
  const int stepLength = 5;   // Verschiebung der LEDs um jeweils 5
  // Berechne das Intervall für Mode 3 (extra Intervall nur für Mode 3)
  const unsigned long intervalMode = 60000 / (BPM * (NUM_LEDS / stepLength));  // Berechne das Intervall für Mode 3

  if (currentMillis - lastUpdateTime > intervalMode)
  {
    fill_solid(leds, NUM_LEDS, CRGB::Black);  // Alle LEDs ausschalten

    // Setze 5 LEDs gleichzeitig an, beginnend mit der aktuellen Position
    for (int i = 0; i < trailLength; i++)
    {
      int ledPos = position1 + i;

      if (ledPos < NUM_LEDS)
      { leds[ledPos] = colors[colorMode]; }  // Färbe die LED in der gewählten Farbe
    }

    position1 += stepLength;  // Verschiebe die Position um 5 LEDs

    // Wenn das Ende des Stripes erreicht ist, beginne wieder am Anfang
    if (position1 >= NUM_LEDS)
    { position1 = 0; }  // Zurücksetzen auf den Anfang

    lastUpdateTime = currentMillis;  // Speichere die aktuelle Zeit
    FastLED.show();
  }
}

void setModeFiveBPM()
{
  // Mode 4: Rückwärts Lauflicht (beginnend vom Ende)
  const int trailLength = 15;  // Immer 5 LEDs gleichzeitig an
  const int stepLength = 5;   // Verschiebung der LEDs um jeweils 5
  // Berechne das Intervall für Mode 4 (extra Intervall nur für Mode 4)
  const unsigned long intervalMode = 60000 / (BPM * (NUM_LEDS / stepLength));  // Berechne das Intervall für Mode 4

  if (currentMillis - lastUpdateTime > intervalMode)
  {
    fill_solid(leds, NUM_LEDS, CRGB::Black);  // Alle LEDs ausschalten

    // Setze 5 LEDs gleichzeitig an, beginnend mit der aktuellen Position
    for (int i = 0; i < trailLength; i++)
    {
      int ledPos = position2 - i;

      if (ledPos >= 0)
      { leds[ledPos] = colors[colorMode]; }  // Färbe die LED in der gewählten Farbe
    }

    position2 -= stepLength;  // Verschiebe die Position um 5 LEDs rückwärts

    // Wenn das Ende des Stripes erreicht ist, beginne wieder am Ende
    if (position2 < 0)
    { position2 = NUM_LEDS - 1; }  // Zurücksetzen auf das Ende

    FastLED.show();
    lastUpdateTime = currentMillis;  // Speichere die aktuelle Zeit
  }
}

void setModeSixBPM()
{
  // Mode 5: Zwei Lauflichter, die von beiden Seiten zur Mitte laufen und verschwinden, aber immer wieder von vorne starten
  const int trailLength = 10;  // Immer 15 LEDs gleichzeitig an
  const int stepLength = 5;   // Verschiebung der LEDs um jeweils 5
  // Berechne das Intervall für Mode 5
  unsigned long intervalMode5 = 60000 / (BPM * (NUM_LEDS / (stepLength * 2))); // Berechne das Intervall für Mode 5

  if (millis() - lastUpdateTime > intervalMode5)
  {
    fill_solid(leds, NUM_LEDS, CRGB::Black);  // Alle LEDs ausschalten

    // Setze 15 LEDs gleichzeitig an, beginnend von der linken Seite
    for (int i = 0; i < trailLength; i++)
    {
      int ledPos = positionLeft1 + i;

      if (ledPos < NUM_LEDS)
      { leds[ledPos] = colors[colorMode]; }  // Färbe die LED in der gewählten Farbe
    }

    // Setze 15 LEDs gleichzeitig an, beginnend von der rechten Seite
    for (int i = 0; i < trailLength; i++)
    {
      int ledPos = positionRight1 - i;

      if (ledPos >= 0)
      { leds[ledPos] = colors[colorMode]; }  // Färbe die LED in der gewählten Farbe
    }

    // Berechne den Abstand zwischen den beiden Positionen (soll nach und nach kleiner werden)
    int distance = positionRight1 - positionLeft1;

    // Verschiebe die Positionen langsamer zusammen, sodass sich die LEDs mehr in der Mitte treffen
    if (distance > trailLength)
    {
      positionLeft1 += stepLength;  // Verschiebe die Position nach rechts
      positionRight1 -= stepLength;  // Verschiebe die Position nach links
    }

    // Wenn die Mitte erreicht ist, beginne wieder von vorne
    if (distance <= trailLength)
    {
      // Setze die Positionen zurück, um den Effekt neu zu starten
      positionLeft1 = 0;
      positionRight1 = NUM_LEDS - 1;
    }

    FastLED.show();
    lastUpdateTime = currentMillis;  // Speichere die aktuelle Zeit
  }
}

void setModeSevenBPM()
{
  // Mode 6: Zwei Lauflichter, die von der Mitte nach außen laufen und immer wieder von vorne starten
  const int trailLength = 10;  // Immer 5 LEDs gleichzeitig an
  const int stepLength = 5;   // Verschiebung der LEDs um jeweils 5
  // Berechne das Intervall für Mode 6
  const unsigned long intervalMode6 = 60000 / (BPM * (NUM_LEDS / (stepLength * 2)));  // Berechne das Intervall für Mode 6

  if (currentMillis - lastUpdateTime > intervalMode6)
  {
    fill_solid(leds, NUM_LEDS, CRGB::Black);  // Alle LEDs ausschalten

    // Setze 5 LEDs gleichzeitig an, beginnend von der linken Mitte
    for (int i = 0; i < trailLength; i++)
    {
      int ledPos = positionLeft2 - i;

      if (ledPos >= 0)
      { leds[ledPos] = colors[colorMode]; }  // Färbe die LED in der gewählten Farbe
    }

    // Setze 5 LEDs gleichzeitig an, beginnend von der rechten Mitte
    for (int i = 0; i < trailLength; i++)
    {
      int ledPos = positionRight2 + i;

      if (ledPos < NUM_LEDS)
      { leds[ledPos] = colors[colorMode]; }  // Färbe die LED in der gewählten Farbe
    }

    // Verschiebe die Positionen nach außen, sodass sich die LEDs in beide Richtungen ausbreiten
    if (positionLeft2 > 0 && positionRight2 < NUM_LEDS - 1)
    {
      positionLeft2 -= stepLength;  // Verschiebe die Position nach links
      positionRight2 += stepLength;  // Verschiebe die Position nach rechts
    }

    // Wenn beide Positionen das Ende erreichen, beginne wieder von der Mitte
    if (positionLeft2 <= 0 || positionRight2 >= NUM_LEDS - 1)
    {
      // Setze die Positionen zurück, um den Effekt neu zu starten
      positionLeft2 = halfLength;
      positionRight2 = halfLength;
    }

    FastLED.show();
    lastUpdateTime = currentMillis;  // Speichere die aktuelle Zeit
  }
}

void setLedStripe()
{
  static uint8_t oldBPM = 0;
  static uint8_t oldMode = 255;
  static uint8_t oldColorMode = 255;

  if (oldMode != mode)
  {
    oldBPM = 0;
    oldColorMode = 255;
    fill_solid(leds, NUM_LEDS, CRGB::Black);  // Clear bei Moduswechsel
    FastLED.show();
  }

  bool colorOrBPMChanged = (oldBPM != BPM) || (oldColorMode != colorMode);

  // Clear LEDs nur bei relevanten Änderungen für Mode 0/1
  if ((mode == 0 || mode == 1) && colorOrBPMChanged) {
    fill_solid(leds, NUM_LEDS, CRGB::Black);
  }

  switch (mode)
  {
    case 0:
      if (colorOrBPMChanged) {
        setModeNullBPM();
      }
      break;

    case 1:
      if (colorOrBPMChanged) {
        setModeOneBPM();
      }
      break;

    case 2: setModeTwoBPM(); break;
    case 3: setModeThreeBPM(); break;
    case 4: setModeFourBPM(); break;
    case 5: setModeFiveBPM(); break;
    case 6: setModeSixBPM(); break;
    case 7: setModeSevenBPM(); break;
  }

  oldMode = mode;
  oldBPM = BPM;
  oldColorMode = colorMode;
}

laufen beide Arduinos mit den gleichen DMX Kanälen oder unterscheiden sie sich?

Vermutung: beim mode 0&1 wird FastLED.show(); in jedem loop-Durchlauf aufgerufen und ausgeführt. Bei den Animationen nur nach Ablauf von bestimmten Intervallen.
Die Ansteuerung von WS281X-LEDs blockiert (meines Wissens nach) einige andere Funktionen (Serielle Kommunikation, Interrupts u.ä.). Eventuell macht das den Receiver "schwerhörig" für einkommende Signale?
Also im modus 0&1 nur dann .show() aufrufen, wenn sich tatsächlich etwas geändert hat?

1 Like

Also ich versuchen ihnen zu helfen aber mein deutsch is nicht meine sprache.
Zu erst.

Den Kable sollte ein verdreht paar sein. Nicht das problem das du hast aber das kan in zukunft problemen geben.

Weil mann ein signal zu ledstreifen sendet, wirden interrupts auf geschaltet und DMX entfangst braucht sie. Das ist die schwierigkeit. Ich kan es fur sie losen aber nicht auf deutsch. Wenn du ein topic anfangt auf english oder im PM dan mach ich weiter.

1 Like
#include <FastLED.h>
#include <DMXSerial.h>

constexpr uint8_t LED_PIN {2};
constexpr uint8_t BTN_MODE {4};  // Taster für Moduswechsel
constexpr uint8_t BTN_COLOR {6};  // Taster für Farbwechsel
constexpr uint8_t BTN_SPEED {8};  // Taster für Geschwindigkeitswechsel

constexpr uint8_t NUM_LEDS {118};
constexpr uint8_t halfLength {NUM_LEDS / 2};
constexpr uint8_t thirdLength {NUM_LEDS / 3};
constexpr uint8_t quarterLength {NUM_LEDS / 4};
constexpr uint8_t fifthLength {NUM_LEDS / 5};

CRGB leds[NUM_LEDS];

// DMX-Kanäle
constexpr uint8_t dmxMode {14};
constexpr uint8_t dmxColor {15};
constexpr uint8_t dmxBPM {16};

//Standartwerte
int mode = 0;
int colorMode = 0;
int BPM = 95;

int lastMode = -1;
int lastBPM = 95;

byte hue = 0;
unsigned long interval = 60000 / (BPM * NUM_LEDS);  // Startwert für das Intervall
unsigned long lastModeChange = 0;
unsigned long lastColorChange = 0;
unsigned long lastSpeedChange = 0;
unsigned long debounceDelay = 350;
unsigned long currentMillis = 0;

//DMX Werte
int dmxModeVal = 0;
int dmxColorVal = 0;
int dmxBPMVal = 0;

//Globale Variablen für Modis
bool firstHalfOn = true;  // Variable, um festzustellen, welche Hälfte gerade an ist
unsigned long lastHalfChange = 0;  // Letztes Wechselzeitpunkt der Hälften

unsigned long lastUpdateTime = 0;
int position1 = 0;  // Startposition für das erste Segment

int position2 = NUM_LEDS - 1;  // Startposition am Ende des Stripes

int positionLeft1 = 0;  // Startposition von links
int positionRight1 = NUM_LEDS - 1;  // Startposition von rechts

int positionLeft2 = halfLength;  // Startposition von der Mitte links
int positionRight2 = halfLength;  // Startposition von der Mitte rechts

// Farben-Array für den Regenbogen (einschließlich Weiß am Anfang)
CRGB colors[] =
{
  CRGB::White,           // Weiß
  CRGB(255, 0, 0),       // Rot
  CRGB(255, 110, 0),     // Orange
  CRGB(255, 255, 0),     // Gelb
  CRGB(0, 255, 0),       // Grün
  CRGB(70, 130, 180),    // Stahlblau (Steel Blue)
  CRGB(0, 0, 255),       // Blau
  CRGB(75, 0, 130),      // Indigo
  CRGB(255, 20, 147)     // Pink
};

void setup()
{
  // FastLED Setup
  FastLED.addLeds < WS2812B, LED_PIN, GRB > (leds, NUM_LEDS);
  FastLED.setBrightness(200);
  // DMX Setup
  DMXSerial.init(DMXReceiver); // Aktiviert den DMX-Receiver
  // Pin Setup
  pinMode(BTN_MODE, INPUT_PULLUP);   // Taster für Moduswechsel
  pinMode(BTN_COLOR, INPUT_PULLUP);  // Taster für Farbwechsel
  pinMode(BTN_SPEED, INPUT_PULLUP);  // Taster für Geschwindigkeitswechsel bzw. Segmentwechsel (mode 0 u 1)
}

void resetModeState()
{
  // Modus 3
  firstHalfOn = true;
  lastHalfChange = 0;
  // Modus 4
  lastUpdateTime = 0;
  position1 = 0;
  // Modus 5
  position2 = NUM_LEDS - 1;
  // Modus 6
  positionLeft1 = 0;
  positionRight1 = NUM_LEDS - 1;
  // Modus 7
  positionLeft2 = halfLength;
  positionRight2 = halfLength;
  // Reset Hue für Modus 2
  hue = 0;
}

void dmxModeControl()
{
  // Modesteuerung
  if (dmxModeVal != 0)
  { mode = map(dmxModeVal, 0, 255, 0, 7); }
  else if (currentMillis - lastModeChange > debounceDelay)
  {
    if (digitalRead(BTN_MODE) == LOW)
    {
      mode = (mode + 1) % 8;
      lastModeChange = currentMillis;
    }
  }

  if (mode != lastMode)
  {
    resetModeState();
    lastMode = mode;
  }
}

void dmxColorControl()
{
  // Farbsteuerung
  if (dmxColorVal != 0)
  { colorMode = map(dmxColorVal, 0, 255, 0, 8); }
  else if (currentMillis - lastColorChange > debounceDelay)
  {
    if (digitalRead(BTN_COLOR) == LOW)
    {
      colorMode = (colorMode + 1) % 9;
      lastColorChange = currentMillis;
    }
  }
}

void dmxBPMControl()
{
  // BPM / Segmentsteuerung
  if (dmxBPMVal != 0)
  {
    switch (dmxBPMVal)
    {
      case 0 ... 31: BPM = 95; break;

      case 32 ... 63: BPM = 110; break;

      case 64 ... 95: BPM = 125; break;

      case 96 ... 127: BPM = 140; break;

      case 128 ... 159: BPM = 155; break;

      case 160 ... 191: BPM = 170; break;

      case 192 ... 223: BPM = 185; break;

      case 224 ... 255: BPM = 200; break;
    }
  }
  else if (currentMillis - lastSpeedChange > debounceDelay)
  {
    if (digitalRead(BTN_SPEED) == LOW)
    {
      BPM = (BPM + 15 > 200) ? 95 : BPM + 15;
      lastSpeedChange = currentMillis;
    }

    if (BPM != lastBPM)
    {
      resetModeState();
      lastBPM = BPM;
    }
  }
}

void loop()
{
  currentMillis = millis();
  // DMX-Daten
  dmxModeVal = DMXSerial.read(dmxMode);
  dmxColorVal = DMXSerial.read(dmxColor);
  dmxBPMVal = DMXSerial.read(dmxBPM);
  dmxModeControl();
  dmxColorControl();
  dmxBPMControl();
  setLedStripe();
}

void setModeNullBPM()
{
  const int segments = 10;
  const int segmentLength = NUM_LEDS / segments;

  switch (BPM)
  {
    case 95:
      // Alle LEDs in der gewählten Farbe
      fill_solid(leds, NUM_LEDS, colors[colorMode]);
      break;

    case 110:

      // Erstes Drittel an
      for (int i = 0; i < thirdLength; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 125:

      // Zweites Drittel an
      for (int i = thirdLength; i < 2 * thirdLength; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 140:

      // Drittes Drittel an
      for (int i = 2 * thirdLength; i < NUM_LEDS; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 155:

      // Erstes und drittes Viertel an
      for (int i = 0; i < quarterLength; i++)
      { leds[i] = colors[colorMode]; }

      for (int i = 2 * quarterLength; i < 3 * quarterLength; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 170:

      // Zweites und viertes Viertel an
      for (int i = quarterLength; i < 2 * quarterLength; i++)
      { leds[i] = colors[colorMode]; }

      for (int i = 3 * quarterLength; i < NUM_LEDS; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 185:

      // Jedes 1., 3., 5. Segment an
      for (int i = 0; i < segments; i++)
      {
        if (i % 2 == 0)
        {
          int startIdx = i * segmentLength;
          int endIdx = (i + 1) * segmentLength - 1;

          if (i == segments - 1)
          {
            endIdx = NUM_LEDS - 1;

            for (int j = startIdx; j <= endIdx; j++)
            { leds[j] = colors[colorMode]; }
          }
        }

        if (NUM_LEDS % segments != 0)
        {
          int remainingStartIdx = segments * segmentLength;

          for (int j = remainingStartIdx; j < NUM_LEDS; j++)
          {
            leds[j] = colors[colorMode];
          }
        }
      }

      break;

    case 200:

      // Jedes 2., 4., 6. Segment an
      for (int i = 0; i < segments; i++)
      {
        if (i % 2 != 0)
        {
          int startIdx = i * segmentLength;
          int endIdx = (i + 1) * segmentLength - 1;

          for (int j = startIdx; j <= endIdx; j++)
          { leds[j] = colors[colorMode]; }
        }
      }

      break;
  }

  FastLED.show();
}

void setModeOneBPM()
{
  switch (BPM)
  {
    case 95:    // Effekt 1: Alle LEDs aus
      break;

    case 110:    // Effekt 2: Erste Hälfte an
      for (int i = 0; i < halfLength; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 125:    // Effekt 3: Zweite Hälfte an
      for (int i = halfLength; i < NUM_LEDS; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 140:    // Effekt 7: Erstes Fünftel an
      for (int i = 0; i < fifthLength; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 155:    // Effekt 8: Zweites Fünftel an
      for (int i = fifthLength; i < 2 * fifthLength; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 170:    // Effekt 9: Drittes Fünftel an
      for (int i = 2 * fifthLength; i < 3 * fifthLength; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 185:    // Effekt 10: Viertes Fünftel an
      for (int i = 3 * fifthLength; i < 4 * fifthLength; i++)
      { leds[i] = colors[colorMode]; }

      break;

    case 200:    // Effekt 11: Fünftes Fünftel an
      for (int i = 4 * fifthLength; i < NUM_LEDS; i++)
      { leds[i] = colors[colorMode]; }

      break;
  }

  FastLED.show();
}

void setModeTwoBPM()
{
  static unsigned long lastHueUpdate = 0;  // Speichert den letzten Update-Zeitpunkt
  unsigned long currentInterval = interval * 5;  // Intervall basierend auf BPM

  if (currentMillis - lastHueUpdate > currentInterval)
  {
    fill_solid(leds, NUM_LEDS, CHSV(hue, 255, 255));
    hue++;

    if (hue >= 255)
    { hue = 0; }

    lastHueUpdate = currentMillis;  // Aktualisiere den Timer
    FastLED.show();
  }
}

void setModeThreeBPM()
{
  // Modus 2: Abwechselnd die erste Hälfte an und die zweite Hälfte, abhängig von BPM
  interval = 60000 / BPM;

  if (currentMillis - lastHalfChange >= interval)
  {
    if (firstHalfOn)
    {
      // Erste Hälfte an, zweite Hälfte aus
      for (int i = 0; i < halfLength; i++)
      { leds[i] = colors[colorMode]; }  // Erste Hälfte in der gewählten Farbe

      for (int i = halfLength; i < NUM_LEDS; i++)
      { leds[i] = CRGB::Black; }  // Zweite Hälfte aus
    }
    else
    {
      // Zweite Hälfte an, erste Hälfte aus
      for (int i = 0; i < halfLength; i++)
      { leds[i] = CRGB::Black; }  // Erste Hälfte aus

      for (int i = halfLength; i < NUM_LEDS; i++)
      { leds[i] = colors[colorMode]; }  // Zweite Hälfte in der gewählten Farbe
    }

    FastLED.show();  // LEDs aktualisieren
    firstHalfOn = !firstHalfOn;  // Wechseln zwischen den Hälften
    lastHalfChange = currentMillis;  // Speichere das aktuelle Zeitstempel
  }
}

void setModeFourBPM()
{
  // Mode 3: Lauflicht mit immer 5 leuchtenden LEDs (verschiebt sich schrittweise)
  const int trailLength = 15;  // Immer 5 LEDs gleichzeitig an
  const int stepLength = 5;   // Verschiebung der LEDs um jeweils 5
  // Berechne das Intervall für Mode 3 (extra Intervall nur für Mode 3)
  const unsigned long intervalMode = 60000 / (BPM * (NUM_LEDS / stepLength));  // Berechne das Intervall für Mode 3

  if (currentMillis - lastUpdateTime > intervalMode)
  {
    fill_solid(leds, NUM_LEDS, CRGB::Black);  // Alle LEDs ausschalten

    // Setze 5 LEDs gleichzeitig an, beginnend mit der aktuellen Position
    for (int i = 0; i < trailLength; i++)
    {
      int ledPos = position1 + i;

      if (ledPos < NUM_LEDS)
      { leds[ledPos] = colors[colorMode]; }  // Färbe die LED in der gewählten Farbe
    }

    position1 += stepLength;  // Verschiebe die Position um 5 LEDs

    // Wenn das Ende des Stripes erreicht ist, beginne wieder am Anfang
    if (position1 >= NUM_LEDS)
    { position1 = 0; }  // Zurücksetzen auf den Anfang

    lastUpdateTime = currentMillis;  // Speichere die aktuelle Zeit
    FastLED.show();
  }
}

void setModeFiveBPM()
{
  // Mode 4: Rückwärts Lauflicht (beginnend vom Ende)
  const int trailLength = 15;  // Immer 5 LEDs gleichzeitig an
  const int stepLength = 5;   // Verschiebung der LEDs um jeweils 5
  // Berechne das Intervall für Mode 4 (extra Intervall nur für Mode 4)
  const unsigned long intervalMode4 = 60000 / (BPM * (NUM_LEDS / stepLength));  // Berechne das Intervall für Mode 4

  if (millis() - lastUpdateTime > intervalMode4)
  {
    fill_solid(leds, NUM_LEDS, CRGB::Black);  // Alle LEDs ausschalten

    // Setze 5 LEDs gleichzeitig an, beginnend mit der aktuellen Position
    for (int i = 0; i < trailLength; i++)
    {
      int ledPos = position2 - i;

      if (ledPos >= 0)
      { leds[ledPos] = colors[colorMode]; }  // Färbe die LED in der gewählten Farbe
    }

    position2 -= stepLength;  // Verschiebe die Position um 5 LEDs rückwärts

    // Wenn das Ende des Stripes erreicht ist, beginne wieder am Ende
    if (position2 < 0)
    { position2 = NUM_LEDS - 1; }  // Zurücksetzen auf das Ende

    FastLED.show();
    lastUpdateTime = millis();  // Speichere die aktuelle Zeit
  }
}

void setModeSixBPM()
{
  // Mode 5: Zwei Lauflichter, die von beiden Seiten zur Mitte laufen und verschwinden, aber immer wieder von vorne starten
  const int trailLength = 10;  // Immer 15 LEDs gleichzeitig an
  const int stepLength = 5;   // Verschiebung der LEDs um jeweils 5
  // Berechne das Intervall für Mode 5
  unsigned long intervalMode5 = 60000 / (BPM * (NUM_LEDS / (stepLength * 2))); // Berechne das Intervall für Mode 5

  if (millis() - lastUpdateTime > intervalMode5)
  {
    fill_solid(leds, NUM_LEDS, CRGB::Black);  // Alle LEDs ausschalten

    // Setze 15 LEDs gleichzeitig an, beginnend von der linken Seite
    for (int i = 0; i < trailLength; i++)
    {
      int ledPos = positionLeft1 + i;

      if (ledPos < NUM_LEDS)
      { leds[ledPos] = colors[colorMode]; }  // Färbe die LED in der gewählten Farbe
    }

    // Setze 15 LEDs gleichzeitig an, beginnend von der rechten Seite
    for (int i = 0; i < trailLength; i++)
    {
      int ledPos = positionRight1 - i;

      if (ledPos >= 0)
      { leds[ledPos] = colors[colorMode]; }  // Färbe die LED in der gewählten Farbe
    }

    // Berechne den Abstand zwischen den beiden Positionen (soll nach und nach kleiner werden)
    int distance = positionRight1 - positionLeft1;

    // Verschiebe die Positionen langsamer zusammen, sodass sich die LEDs mehr in der Mitte treffen
    if (distance > trailLength)
    {
      positionLeft1 += stepLength;  // Verschiebe die Position nach rechts
      positionRight1 -= stepLength;  // Verschiebe die Position nach links
    }

    // Wenn die Mitte erreicht ist, beginne wieder von vorne
    if (distance <= trailLength)
    {
      // Setze die Positionen zurück, um den Effekt neu zu starten
      positionLeft1 = 0;
      positionRight1 = NUM_LEDS - 1;
    }

    FastLED.show();
    lastUpdateTime = currentMillis;  // Speichere die aktuelle Zeit
  }
}

void setModeSevenBPM()
{
  // Mode 6: Zwei Lauflichter, die von der Mitte nach außen laufen und immer wieder von vorne starten
  const int trailLength = 10;  // Immer 5 LEDs gleichzeitig an
  const int stepLength = 5;   // Verschiebung der LEDs um jeweils 5
  // Berechne das Intervall für Mode 6
  const unsigned long intervalMode6 = 60000 / (BPM * (NUM_LEDS / (stepLength * 2)));  // Berechne das Intervall für Mode 6

  if (currentMillis - lastUpdateTime > intervalMode6)
  {
    fill_solid(leds, NUM_LEDS, CRGB::Black);  // Alle LEDs ausschalten

    // Setze 5 LEDs gleichzeitig an, beginnend von der linken Mitte
    for (int i = 0; i < trailLength; i++)
    {
      int ledPos = positionLeft2 - i;

      if (ledPos >= 0)
      { leds[ledPos] = colors[colorMode]; }  // Färbe die LED in der gewählten Farbe
    }

    // Setze 5 LEDs gleichzeitig an, beginnend von der rechten Mitte
    for (int i = 0; i < trailLength; i++)
    {
      int ledPos = positionRight2 + i;

      if (ledPos < NUM_LEDS)
      { leds[ledPos] = colors[colorMode]; }  // Färbe die LED in der gewählten Farbe
    }

    // Verschiebe die Positionen nach außen, sodass sich die LEDs in beide Richtungen ausbreiten
    if (positionLeft2 > 0 && positionRight2 < NUM_LEDS - 1)
    {
      positionLeft2 -= stepLength;  // Verschiebe die Position nach links
      positionRight2 += stepLength;  // Verschiebe die Position nach rechts
    }

    // Wenn beide Positionen das Ende erreichen, beginne wieder von der Mitte
    if (positionLeft2 <= 0 || positionRight2 >= NUM_LEDS - 1)
    {
      // Setze die Positionen zurück, um den Effekt neu zu starten
      positionLeft2 = halfLength;
      positionRight2 = halfLength;
    }

    FastLED.show();
    lastUpdateTime = currentMillis;  // Speichere die aktuelle Zeit
  }
}

void setLedStripe()
{
  // Aktualisiere das Intervall mit der neuen BPM
  // interval = 60000 / (BPM * NUM_LEDS);
  static uint8_t oldBPM = 0;
  static uint8_t oldMode = 255;

  if (oldMode != mode)
  { oldBPM = 0; }

  if (oldBPM != BPM)
  { fill_solid(leds, NUM_LEDS, CRGB::Black); }    // Zuerst alle LEDs schwarz setzen

  // Modi im Switch-Case-Block behandeln
  switch (mode)
  {
    case 0:
      if (oldBPM != BPM)
      { setModeNullBPM(); }

      break;

    case 1:
      if (oldBPM != BPM)
      { setModeOneBPM(); }

      break;

    case 2: setModeTwoBPM(); break;

    case 3: setModeThreeBPM(); break;

    case 4: setModeFourBPM(); break;

    case 5: setModeFiveBPM(); break;

    case 6: setModeSixBPM(); break;

    case 7: setModeSevenBPM(); break;
  }

  oldMode = mode;
  oldBPM = BPM;
}
1 Like

@Deva_Rishi: Our moderator @uwefed will accept Your answer in English, if You have a good solution. I hope so :wink:

If the threat owner has problems in translating, some people are able to help.

Let's try it!

@noiasca ja die liegen beide auf den gleichen Kanälen.

@derGeppi habe das nun so geändert. Deine Vermutung war richtig, habe Mode 0 & 1 nun so eingestellt, dass nur ein FastLED.show() ausgeführt wird, wenn sich auch wirklich etwas ändert. Damit ich das aber auch verstehe, das heißt ja dann nichts anderes, dass man den optimale Zeitspanne finden muss, damit die Laufanimationen flüssig sind, aber nicht zu schnell erneuert werden, sodass die DMX Daten eingelesen werden können und nicht durch FastLED.show() unterbrochen werden? Und wenn bei den Laufanimationen der seltene Fall auftritt, dass diese minimal unterschiedlich starten hat dann quasi ein Controller in dem Moment, wo sich der DMX Wert ändert ein FastLED.show() ausgeführt?

Nachdem ich nun das genau Problem kenne, habe ich durch Recherche gesehen, dass das Problem bekannt ist und FastLED.h Möglichkeiten bietet kurze interrupts zuzulassen, um die DMX Kommunikation zuverlässiger zu machen, das werde ich gleich mal ausprobieren

@Deva_Rishi i am also fine with english, derGeppi & your answer were correct, after i changed the code, that FastLED.show() is not in every loop cycle it changes way faster, but just for interests, which problems could the cable cause in the future? So the clue of that is to get the optimum time between FastLED.show() for a smooth animation, but not too often so the DMX is not blocked?

@my_xy_projekt habe es mit dem Code versucht gehabt, das Problem war immernoch vorhanden, habe aber durch die vorherigen Antworten schon eine Lösung.

Danke an alle die so schnell geholfen haben dieses Problem zu lösen. Hat auf jedenfall Verständnis gebracht. :partying_face:

Das versteh ich nicht, denn

Ich mache genau das.

1 Like

@my_xy_projekt
Okay ich nehme alles zurück, habe den BPM bereich nochmal angepasst, ohne das ... mit switch case und dmx/32 dann funktioniert es ebenfalls. Hier muss ich dann nurnoch im setLedStripe ergänzen, dass auch eine Farbänderung ein erneutes setMode auslöst, da sonst die Farbe nur aktualisiert wird, wenn BPM bzw. Modus geändert wird.

Ich hatte am Morgen angefangen das komplett in schön zu machen...
Musste mich aber erstmal an die Logik gewöhnen.

Macht es Sinn, Dir das noch weiter zu bauen? (Würde dann in der nächsten Nacht)

1 Like

Also du musst natürlich nichts, ist natürlich dir überlassen, was ich jedoch sagen muss, dass dein Code deutlich schöner aufgebaut ist mit den unterschiedlichen Funktionen. Werde mich da jetzt auch wieder ransetzen und schauen in wiefern ich das alles hinbekomme.

Na mal schauen. Wenn Dich das aufteilen in Funktionen schon gefreut hat, mach ich Dir das fertig.

OK great, i have had discussions about this on the forum, the main thing is that the native speakers are usually better at doing the translation.

The main problem with digital data signal transfer (and even with analog ones) is capacitance of the cable. Electrons pile up on the surface of the metal and the cable starts to act as a capacitor. This causes the data-signal, which is a square wave, to get softer edges. The longer the cable, the thicker the cable, & the more strands in the cable, The higher the capacitance will be. When the capacitance is higher, a lower data transfer speed can be supported.
Basically the MAX485 changes the TTL input to a system where the A & B outputs change polarity depending on the input logic level. The Transceiver at the other end measures the potential difference and converts the polarity back to a TTL logic level. The idea is behind the twisted pair is that the magnetic effect of the coils of a twisted pair counteract the capacitance, and as such increase the possible length.

DMX is rated for a network of up to 100 meters (in a hostile environment, which a club is.. which it's speakers !)
If you do the math one would find that in theory, the 250Kbps that DMX uses, could be supporting something up to 320 meters, but of course there are all sorts of XLR plugs in the chain as well, so 100 meters should be a safe limit.
Using the transceivers add the benefit of not requiring a common Ground, since the A & B lines effectively reference each other, which can prevent ( it is not a galvanic isolation, so 'can' prevent ) damage to multiple devices in case of the failure of 1 device.
Adding a non-twisted pair cable in the chain can reduce the possible length of the DMX chain because of the lack of the electro-magnetic counter effect. Add to this that as far as capacitance goes, side branches also add to it significantly. It is also important to use a terminator(which is just a 120R resistor) to assure that 'some' current flows through the pair, or the whole thing doesn't work properly. So when i say in the future, i mean when you start inserting it into a chain or when your chain increases in length.

So about the issue at hand. You stated that FastLED has keeping interrupts enabled as an option, which should improve DMX reception, but may cause other faults.
The libary that you use for DMX reception, does reception non-stop but i suspect just discards any frame that may have an error. I haven't look at the internal workings of it, but you have not reported any incorrect data being received, so that would be my conclusion.
DMX reception consists of 2 stages. First, break detection, and then data reception. A break of 88us minimum is sent before a new frame is sent. After that all channels are transmitted in order. Channels 0 - 512, 0 is a dummy channel, and DMX allows transmission of an incomplete frame.
Just storing the data from the UART FIFO into memory is a process that depends on interrupts. Once there is a byte there to be transferred into memory, it must be done before the next byte arrives or 1 of the 2 bytes is lost, particularly with an AVR mcu like the 328p which has a 1 byte FIFO. Also break-detection will require interrupts to work.
I used (and up to some extend still do use) a system where first a DMX-frame is received, (or at least the relevant channels for my purpose) . Then it is processed. and possibly a LED-string signal is transmitted, after which the MCU will start looking for the 'break' and receive the next frame.
Depending on the amount of processing and the last channel i want to receive, i may even get the same output framerate as DMX which is about 40Hz, but any other frame is almost for sure attainable.

Thing is that means that animations will have to be non-blocking in nature, since DMX reception needs to 'explicitly' happen synchronously, rather than a-synchronously as it is done with DMXSerial.
I think yours are though. You are effectively receiving only 3 channels, but DMXSerial is receiving and storing the whole frame and keeps the processor occupied.
I started out with this method and optimized it a bit over the years and it no longer requires fiddling with the core, just the manually setting of the registers

This is if i am correct, the last version i have

#define PWM_LED_PIN 5
#define DMX_PING 13
#define MAX_DMX_CHANNELS 512

#define NUMBER_OF_CHANNELS 510

#define BAUDDMX 250000UL

#define RECEIVE_FORMAT 0x06 // 8N1 (could be 8N2 aswell.

#define UBDMX ((F_CPU / (8 * BAUDDMX)) - 1)



volatile uint16_t dmxaddress = 1;
volatile uint16_t i = 0;
volatile uint8_t dmxreceived = 0;
volatile uint16_t dmxcurrent = 0;
volatile uint8_t dmxvalue[NUMBER_OF_CHANNELS];

volatile uint8_t dmxnewvalue = 0;
volatile uint8_t zerocounter = 0;
volatile bool storeSerial = false;

void setup() {

  pinMode(PWM_LED_PIN, OUTPUT); // first dmxchannel LED value
  pinMode(DMX_PING, OUTPUT); //ping LED
  init_uart();
  SetupTimerRegisters();
}

void loop()  {
  ReceiveDMX();
  Processing();

}

void ReceiveDMX() {
  dmxnewvalue = 0;
  dmxcurrent = 0;
  zerocounter = 0;
  i = 0;
  UCSR0C = RECEIVE_FORMAT; //0x06; // 8N1
  bitSet(UCSR0B, RXCIE0);   // enable RX ISR
  bitSet(TIMSK2, OCIE2A);
  while (dmxnewvalue != 1);
}

void Processing() {  // main processing
  static bool pin = true;
  pin = !pin;
  analogWrite(5, dmxvalue[0]);
  digitalWrite(13, pin);
  if (dmxvalue[0] > 200) {
    digitalWrite(13, HIGH);
  }
  else {
    digitalWrite(13, LOW);
  }
}

void init_uart() {
  DDRD |=  (1 << PORTD1); // set TX pin  to output
  DDRD &= ~(1 << PORTD0); // set RX pin to input
  UCSR0A = 0x02; // 1<<U2X | 0<<MPCM;
  UCSR0B = 1 << RXCIE0 | 0 << TXCIE0 | 0 << UDRIE0 | 1 << RXEN0 | 1 << TXEN0 | 0 << UCSZ02; // Enable TX & RX, disable RX interrupt
  UCSR0C = RECEIVE_FORMAT; // 8N1
  UBRR0H = (UBDMX >> 8);  // set baud rate
  UBRR0L = (UBDMX & 0xFF); // HL register
}

void SetupTimerRegisters() {  // Sets up Timer2 to fire every 4us
  cli();
  bitClear(TCCR2A, COM2A1);
  bitClear(TCCR2A, COM2A0);
  bitClear(TCCR2A, COM2B1);
  bitClear(TCCR2A, COM2B0);
  bitSet(TCCR2A, WGM21);
  bitClear(TCCR2A, WGM20);
  bitClear(TCCR2B, FOC2A);
  bitClear(TCCR2B, FOC2B);
  bitClear(TCCR2B, WGM22);
  bitClear(TCCR2B, CS22);
  bitClear(TCCR2B, CS21);
  bitSet(TCCR2B, CS20);
  OCR2A = 64;
  bitClear(TIMSK2, OCIE2B);
  bitSet(TIMSK2, OCIE2A);
  bitClear(TIMSK2, TOIE2);
  sei();
}

ISR(TIMER2_COMPA_vect) {
  if (bitRead(PIND, PIND0)) {  // checks if the pin has gone HIGH
    zerocounter = 0;
  }
  else {
    zerocounter++;
    if (zerocounter == 20)  // if 80us have passed 'Break' has been detected
    {
      bitClear(TIMSK2, OCIE2A);  // disable Timer2 Interrupt
      storeSerial = true;
    }
  }
} //end Timer2 ISR

ISR(USART_RX_vect) {
  dmxreceived = UDR0;
  if (storeSerial) {
    dmxcurrent++;     //increment address counter starts at 0
    // this skips the start code
    if (dmxcurrent > dmxaddress) {        //check if the current address is the one we want.
      dmxvalue[i] = dmxreceived;
      i++;
      if ((i == NUMBER_OF_CHANNELS) || (dmxcurrent > MAX_DMX_CHANNELS - 2)) {
        bitClear(UCSR0B, RXCIE0);                   // disable RX ISR
        storeSerial = false;                      // set the flag to false
        dmxnewvalue = 1;                        //set newvalue, so that the main code can be executed.
      }
    }
  }
} // end ISR

When i say, ' if i am correct' i mean, i haven't used this code for quite a while because i use ESP's to transmit the WS2811 signal these days because that can be done in the background by the I2S peripheral leaving interrupts enabled, and the DMX reception code is significantly different for an ESP (and on an ESP32 even more so)
But i hope this helps !

Nicht zu oft die LEDs via .show() aktualisieren bringt ganz bestimmt Besserung beim Empfang der DMX-Daten, aber birgt halt noch immer die Gefahr, daß sich die beiden Aktionen überschneiden und mal das ein oder andere Signal nicht sofort empfangen wird.
Das ist leider dieser eine erhebliche Nachteil beim Arbeiten mit (vielen) WS281x-LEDs.

Noch eine Idee: ich will jetzt nicht Deine ganze Mühe beim Erstellen der Animationen auf dem Nano über den Haufen werfen, aber vielleicht wäre es auch eine gute Möglichkeit, die Animation (bzw. deren Timing) auf dem DMX-Controller (Master) ablaufen zu lassen und nur ein, zwei Werte zu schicken, die dann auf dem DMX-Receiver einen "frame" des Lichtlaufs aufrufen und anzeigen...

An einem Beispiel:

  • Master möchte auf dem Slave eine Lauflichtanimation darstellen
  • schickt einen Wert (im Rhythmus der Animation) an den Slave, der dann folgendes macht:
  • der Wert sorgt am Slave für einen Animationsschritt + 1, Slave berechnet neues Bild & aktualisiert die LEDs (show)
  • oder: jeder Wert entspricht einem festen Bild/Frame der Animation, gefolgt von einem show
  • Slave wartet auf neuen Wert

Was der Master sendet, könnte dann so aussehen:

DMXadr ........... 0-255 (256 verschiedene Animationen)
DMXadr+1 ......... 0-255 (256 verschiedene Bilder der o.g. Animation)
optional:
DMXadr+2,3,4 ..... 0-255 (Farbwerte RGB)
oder 
DMXadr+2 ......... 0-255 (Farbschemas, die im Nano fest abgelegt sind)

Der Intervall-Ablauf, der also vorher auf dem Nano automatisch abgearbeitet wurde, wird jetzt vom Controller übernommen, der quasi immer nur einen Impuls/Wert für das nächste Bild verschickt. So hätte man, solange diese Impulse nicht superschnell hintereinander kommen (show muss nur 1x komplett ausgeführt sein), eine fast perfekte Synchronität, oder?
Leider weiß ich nicht, wie gut das auf dem Controller/Master umzusetzen ist..

Hier würde mich das Ergebnis deiner Recherche allerdings auch interessieren :innocent:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.