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:
- Knopf = Modus
- Knopf = Farbe
- 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:
- Probleme mit DMX Werten nah an einem anderen Bereich -> Werte mittig genommen -> hat das Problem nicht gelöst.
- 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.
- Im Code einen "Delay" eingebaut, sodass der Modus erst geändert wird, wenn der DMX Wert eine Zeit X stabil bleibt -> hat nichts gebracht.
- Meiner Recherche nach ist ja sowohl das Arduino, als auch DMX schnell genug, sodass quasi keine extrem sichtbaren Synchronitätsunterschiede da sein sollten.
- 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;
}