Ich werde es mal heute Abend ausprobieren. Gebe dann mal Rückmeldung.
Macht sie ja.
FULL_OPEN wird erreicht, wenn bei OPEN_RUNNING erkannt wird, dass der Endschalter ausgelöst ist:
// Wenn der obere Schalter schließt (wird LOW), gilt die Schranke als voll geöffnet.
if (!digitalRead(SWITCH_TOP))
{ state = FULL_OPEN; }
Dasswitch : case Konstrukt führt immer nur den Teil aus, der in state hinterlegt ist. alles andere bleibt links liegen.
Mach das.
Ich werd später auch wieder reinschauen.
Nur mit einem dazu gehörigen break;
Das ist einem Anfänger vielleicht nicht klar.
Wenns fehlt, gibts ne Meldung - die sollte dann aber auch gelesen werden ![]()
Durch switch/case ist das besser geworden ![]()
Wenn die Anzeige der Warnungen in der IDE eingeschaltet ist, was leider nicht Standard ist.
@schrubbismoba11: Bitte Anzeige der Warnungen in der IDE einschalten!
Basierend auf #15:
Programm mit Interrupt
#include <Adafruit_NeoPixel.h>
constexpr uint8_t LED_PIN {6}; // Anschluss für den Neopixel-Streifen
constexpr uint8_t NUM_PIXELS {239}; // Anzahl der LEDs im Streifen (Index 0 bis 239)
constexpr uint8_t SWITCH_BOTTOM {9}; // unterer Endschalter: normal geschlossen (read HIGH)
constexpr uint8_t SWITCH_TOP {10}; // oberer Endschalter: normal offen (read LOW)
constexpr uint8_t TRAFFIC_SIGNAL {2}; // Ampelsignal-Taster an Pin 2 weil wegen interrupt 0
Adafruit_NeoPixel strip(NUM_PIXELS, LED_PIN, NEO_RGB + NEO_KHZ800);
// Zustandsdefinitionen
enum BarrierState
{
FULL_CLOSED, // Schranke voll geschlossen – statisch Rot
OPEN_RUNNING, // Schranke im Öffnungsprozess – Lauflicht (roter Block von 10 LEDs)
FULL_OPEN, // Schranke voll geöffnet – statisch Rot
BLUE_BLINK_WAIT,
BLUE_BLINKING,
CLOSE_RUNNING // Schranke im Schließprozess – Lauflicht (roter Block von 10 LEDs)
};
BarrierState state = FULL_CLOSED;
BarrierState lastState = CLOSE_RUNNING;
constexpr uint32_t rt {0xFF0000};
constexpr uint32_t bl {0x0000FF};
constexpr uint32_t sw {0x000000};
// Variablen zur Erkennung von Zustandsänderungen an den Schaltern
bool prevBottomState;
bool prevTopState;
unsigned long previousMillis = 0;
const unsigned long interval = 25; // Animationsgeschwindigkeit in ms (doppelt so schnell)
int animIndex = 0; // Verschiebungsindex für das Lauflicht
unsigned long signalStartTime = 0;
bool blinking = false;
const unsigned long blinkInterval = 500; // 500 ms für Blinken
unsigned long lastBlinkTime = 0;
bool blinkState = false;
unsigned long blinkStartTime = 0; // Zeitpunkt, an dem das Blinken startet
unsigned long currentMillis = millis();
bool merkerTraffic = false;
void setup()
{
// Schalter als INPUT_PULLUP, sodass definierte Zustände vorliegen:
// - SWITCH_BOTTOM: Normalerweise geschlossen (HIGH), wechselt zu LOW beim Öffnen.
// - SWITCH_TOP: Normalerweise offen (LOW), wechselt zu HIGH beim Schließen.
pinMode(SWITCH_BOTTOM, INPUT_PULLUP); // unterer Endschalter
pinMode(SWITCH_TOP, INPUT_PULLUP); // oberer Endschalter
pinMode(TRAFFIC_SIGNAL, INPUT); // Taster mit Pulldown-Widerstand //INPUT
attachInterrupt(0, pin_ISR, RISING); //interrupt verbinden CHANGE, LOW, FALLING, RISING,
// Initialisierung des LED-Streifens
strip.begin();
strip.show();
// Startzustand: Schranke voll geschlossen (statisch Rot)
setAllPixels(rt);
Serial.begin(9600);
// Initiale Schalterzustände speichern
prevBottomState = digitalRead(SWITCH_BOTTOM); // unterer Endschalter
prevTopState = digitalRead(SWITCH_TOP); // oberer Endschalter
}
void loop()
{
currentMillis = millis();
switch (state)
{
case FULL_CLOSED:
if (state != lastState)
{
setAllPixels(rt); // LED-Band dauerhaft rot
lastState = state;
}
// Wenn der untere Schalter, der normalerweise geschlossen ist, öffnet (Wechsel von LOW zu HIGH)
if (digitalRead(SWITCH_BOTTOM))
{
state = OPEN_RUNNING; // Zustandswechsel: Schranke öffnen
}
break;
case OPEN_RUNNING:
if (state != lastState)
{
setAllPixels(sw);
previousMillis = currentMillis;
animIndex = 0; // Animation zurücksetzen
lastState = state;
}
// Führe die Animation aus, sofern das Intervall erreicht ist
if (currentMillis - previousMillis >= interval)
{
previousMillis = currentMillis;
animateOpen();
}
// Wenn der obere Schalter schließt (wird LOW), gilt die Schranke als voll geöffnet.
if (!digitalRead(SWITCH_TOP))
{
state = FULL_OPEN;
}
break;
// Zustandswechsel: Schranke schließen
case FULL_OPEN:
if (state != lastState)
{
setAllPixels(rt); // LED-Band dauerhaft rot
}
// Wenn der obere Schalter, der geschlossen war, wieder öffnet (Wechsel von LOW zu HIGH)
if (digitalRead(SWITCH_TOP))
{
state = CLOSE_RUNNING;
}
else if (merkerTraffic)
{
merkerTraffic = false;
signalStartTime = currentMillis;
state = BLUE_BLINK_WAIT;
}
break;
case BLUE_BLINK_WAIT:
// Nach 20 Sekunden Blinken starten
if (currentMillis - signalStartTime >= 21000)
{
blinkStartTime = currentMillis; // Zeitpunkt speichern, ab dem das Blinken startet
blinkState = true;
setAllPixels(bl);
state = BLUE_BLINKING;
}
break;
case BLUE_BLINKING:
if (currentMillis - lastBlinkTime >= blinkInterval)
{
lastBlinkTime = currentMillis;
blinkState = !blinkState;
setAllPixels(blinkState ? bl : sw);
}
// Blinken nach 20 Sekunden stoppen (nach Beginn des Blinkens)
if (currentMillis - blinkStartTime >= 28000)
{
setAllPixels(rt); // Zurück zu rot
state = FULL_OPEN;
}
break;
case CLOSE_RUNNING:
if (state != lastState)
{
setAllPixels(sw);
previousMillis = currentMillis;
animIndex = 0; // Animation zurücksetzen
lastState = state;
}
if (currentMillis - previousMillis >= interval)
{
animateClose();
previousMillis = currentMillis;
}
// Wenn der untere Schalter wieder schließt (wird LOW), ist die Schranke voll geschlossen.
if (!digitalRead(SWITCH_BOTTOM))
{
state = FULL_CLOSED;
}
break;
}
}
// Hilfsfunktion: Setzt alle LEDs auf die angegebene Farbe
void setAllPixels(const uint32_t color)
{
strip.fill(color);
strip.show();
}
// Animationsroutine für das Öffnen (Lauflicht von LED 239 bis 0)
void animateOpen()
{
// Alle LEDs ausschalten
for (int i = 0; i < NUM_PIXELS; i++)
{
if (((i + animIndex) / 10) % 2 == 0)
{
strip.setPixelColor(i, rt); // Rot
}
else
{
strip.setPixelColor(i, sw); // Aus
}
}
strip.show();
animIndex = (animIndex + 1) % 20; // Schrittweise verschieben (max. 20 für 10er-Muster)
}
// Animationsroutine für das Schließen (Lauflicht von LED 0 bis 239)
void animateClose()
{
for (int i = 0; i < NUM_PIXELS; i++)
{
if (((i - animIndex) / 10) % 2 == 0)
{
strip.setPixelColor(i, rt); // Rot
}
else
{
strip.setPixelColor(i, sw); // Aus
}
}
strip.show();
animIndex = (animIndex + 1) % 20;
}
void pin_ISR()
{
if (state == FULL_OPEN)
{
merkerTraffic = true;
}
}
Ob ein Interrupt wegen eines kurzen Signals wirklich notwendig ist, kann ich nicht beurteilen, schaden tut er in diesem Programm aber auch nicht.
Ich habe noch strip.fill(color); drin.
Was auch nix anderes macht:
Allerdings vorher noch die Daten von wo bis wo selbst zusammensammelt und zusätzliche Rechenzeit kostet.
Stimmt, Rechenzeit gegen Komfort.
So habe es mal ausprobiert, läuft wie es soll.
Nur habe ich jetzt am Ende beim Dauer offnen die letzten paar Led's nicht mehr rot, sondern rot,gelb,blau.
Denke das liegt aber an der Verkabelung ![]()
Ich hatte absichtlich die ISR rausgenommen....
![]()
Sehr schön.
War heute morgen etwas früh, aber dafür habe ich den Zusammenhang gut erfasst.
Danke fürs rückmelden.
Ich bin beruhigt.
Wenn man jetzt wollte, könnte das noch einwenig eingekürzt werden. Insbesondere, was die Variablen angeht....
Edit:
Ist das nur nach dem öffnen, oder nach dem blau blinken so?
Nach dem Start sind die alle rot?
Nach dem öffnen und blau blinken.
Nach dem Start sind alle rot.
Meinst du die Bezeichnungen?
Es lag an der Verkabelung ![]()
Jetzt mal in denn Raum geworfen, da ich jetzt ja ein Uno r4 WiFi dran habe, ist es möglich diesen von außen zu resetten und oder auch zu überwachen? Webserver oder so?
VG Sascha
Zur Erinnerung, da kenne ich mich nicht mit aus!
Für einen ESP32 beantworte ich Deine Fragen mit Ja.
Bei Fips siehst Du mal in einem Überblick, was so geht, das ist eine Menge.
Oder 250+ ESP32 Projects.
Basierend auf den Tabs von Fips lese ich beispielsweise meinen Stromverbrauch aus, hier den Frühstücks-Cappuccino:
Hier im Forum gab es auch ein Thema zu einer Modellautorennbahn, wo die Preise per WLAN übertragen werden.
Beim ESP32 bekommst Du in diesem Forum mehr Hilfe als beim UNO R4, weshalb ein Umstieg auf ESP32 Dich möglicherweise schneller voran bringt. Später kannst Du den UNO R4 immer noch nutzen.
Wenn Du willst (!), was hält Dich vom Einsatz eines ESP32 ab? Link nur mal als Beispiel.
Auch.
Schau drauf.
Nicht wundern, es fehlt wirklich ein kompletter Funktionsblock.
Der ist der Doppelverwendung zum Opfer gefallen... ![]()
#include <Adafruit_NeoPixel.h>
constexpr uint8_t LED_PIN {6}; // Anschluss für den Neopixel-Streifen
constexpr uint8_t NUM_PIXELS {239}; // Anzahl der LEDs im Streifen (Index 0 bis 239)
constexpr uint8_t SWITCH_BOTTOM {9}; // unterer Endschalter: normal geschlossen (read HIGH)
constexpr uint8_t SWITCH_TOP {10}; // oberer Endschalter: normal offen (read LOW)
constexpr uint8_t TRAFFIC_SIGNAL {2}; // Ampelsignal-Taster
Adafruit_NeoPixel strip(NUM_PIXELS, LED_PIN, NEO_RGB + NEO_KHZ800);
// Zustandsdefinitionen
enum BarrierState
{
FULL_CLOSED, // Schranke voll geschlossen – statisch Rot
OPEN_RUNNING, // Schranke im Öffnungsprozess – Lauflicht (roter Block von 10 LEDs)
FULL_OPEN, // Schranke voll geöffnet – statisch Rot
BLUE_BLINK_WAIT,
BLUE_BLINKING,
CLOSE_RUNNING // Schranke im Schließprozess – Lauflicht (roter Block von 10 LEDs)
};
constexpr uint32_t rt {0xFF0000};
constexpr uint32_t bl {0x0000FF};
constexpr uint32_t sw {0x000000};
constexpr bool dOPEN {true}; // Direction auf
constexpr bool dCLOSE {!dOPEN}; // Direction zu
struct BLINK // was zusammengehört
{
static constexpr uint32_t interval {500};
static constexpr uint32_t wait {21000}; // Wartezeit bis blinken startet
static constexpr uint32_t time {8000}; // Gesamtzeit Blinken
uint32_t millis = 0; // Merker letzter Statuswechsel
bool state = false; // Merker Status
} blink; // Bezeichnung für Container
struct ANIM
{
static constexpr uint32_t interval {25}; // Animationsgeschwindigkeit in ms (doppelt so schnell)
uint32_t millis = 0; // Merker
static constexpr uint8_t pix {10}; // anzahl Pixel mit gleichem Zustand
uint8_t idx = 0; // Verschiebungsindex für das Lauflicht
} anim;
struct STEP
{
uint32_t millis; // Letzter StepChange
BarrierState state; // erklärt sich selbst :-)
BarrierState lastState; //
} step{0, FULL_CLOSED, CLOSE_RUNNING} ;
uint32_t currentMillis;
void setup()
{
// Schalter als INPUT_PULLUP, sodass definierte Zustände vorliegen:
// - SWITCH_BOTTOM: Normalerweise geschlossen (HIGH), wechselt zu LOW beim Öffnen.
// - SWITCH_TOP: Normalerweise offen (LOW), wechselt zu HIGH beim Schließen.
pinMode(SWITCH_BOTTOM, INPUT_PULLUP); // unterer Endschalter
pinMode(SWITCH_TOP, INPUT_PULLUP); // oberer Endschalter
pinMode(TRAFFIC_SIGNAL, INPUT); // Taster mit Pulldown-Widerstand //INPUT
//attachInterrupt(0, pin_ISR, RISING); //interrupt verbinden CHANGE, LOW, FALLING, RISING,
// Initialisierung des LED-Streifens
strip.begin();
strip.show();
// Startzustand: Schranke voll geschlossen (statisch Rot)
setAllPixels(rt);
Serial.begin(9600);
}
void loop()
{
currentMillis = millis();
schranke();
}
void schranke()
{
switch (step.state)
{
case FULL_CLOSED:
if (step.state != step.lastState)
{
setAllPixels(rt); // LED-Band dauerhaft rot
step.lastState = step.state;
}
// Wenn der untere Schalter, der normalerweise geschlossen ist, öffnet (Wechsel von LOW zu HIGH)
if (digitalRead(SWITCH_BOTTOM))
{ step.state = OPEN_RUNNING; }
break;
case OPEN_RUNNING:
if (step.state != step.lastState)
{
setAllPixels(sw);
anim.millis = currentMillis;
anim.idx = 0; // Animation zurücksetzen
step.lastState = step.state;
}
// Führe die Animation aus, sofern das Intervall erreicht ist
if (currentMillis - anim.millis >= anim.interval)
{
anim.millis = currentMillis;
animate(dOPEN);
}
// Wenn der obere Schalter schließt (wird LOW), gilt die Schranke als voll geöffnet.
if (!digitalRead(SWITCH_TOP))
{ step.state = FULL_OPEN; }
break;
case FULL_OPEN:
if (step.state != step.lastState)
{ setAllPixels(rt); }
// Wenn der obere Schalter, der geschlossen war, wieder öffnet (Wechsel von LOW zu HIGH)
if (digitalRead(SWITCH_TOP))
{ step.state = CLOSE_RUNNING; }
else if (digitalRead(TRAFFIC_SIGNAL))
{
step.millis = currentMillis;
step.state = BLUE_BLINK_WAIT;
}
break;
case BLUE_BLINK_WAIT:
if (currentMillis - step.millis >= blink.wait) // Nach Pause Blinken starten
{
blink.millis = currentMillis; // Zeitpunkt speichern, ab dem das Blinken startet
step.millis = currentMillis;
blink.state = true;
setAllPixels(bl);
step.state = BLUE_BLINKING;
}
break;
case BLUE_BLINKING:
if (currentMillis - blink.millis >= blink.interval)
{
blink.millis = currentMillis;
blink.state = !blink.state;
setAllPixels(blink.state ? bl : sw);
}
// Blinken stoppen (nach Beginn des Blinkens)
if (currentMillis - step.millis >= blink.time)
{
setAllPixels(rt);
step.state = FULL_OPEN;
}
break;
case CLOSE_RUNNING:
if (step.state != step.lastState)
{
setAllPixels(sw);
anim.millis = currentMillis;
anim.idx = 0; // Animation gezielter Startpunkt
step.lastState = step.state;
}
if (currentMillis - anim.millis >= anim.interval)
{
animate(dCLOSE);
anim.millis = currentMillis;
}
// Wenn der untere Schalter wieder schließt (wird LOW), ist die Schranke voll geschlossen.
if (!digitalRead(SWITCH_BOTTOM))
{ step.state = FULL_CLOSED; }
break;
}
}
void setAllPixels(const uint32_t color)
{
for (uint8_t i = 0; i < NUM_PIXELS; i++)
{ strip.setPixelColor(i, color); }
strip.show();
}
void animate(const bool dir)
{
for (uint8_t i = 0; i < NUM_PIXELS; i++)
{
int a = dir ? i + anim.idx : i - anim.idx;
if ((a / anim.pix) % 2 == 0)
{ strip.setPixelColor(i, rt); }
else
{ strip.setPixelColor(i, sw); }
}
strip.show();
anim.idx = (anim.idx + 1) % anim.pix * 2; // Schrittweise verschieben
}
erstmal Dankeschön.
Aber jetzt läuft das lauflicht nicht mehr so schön wie bei dem Sketch davor, ist ein wildes geblinke in Rot.
So Läuft es jetzt wieder
anim.idx = (anim.idx + 1) % 20; anim.pix * 2; // Schrittweise verschieben
Der Uno r4 WiFi hat ja den ESP32 on Board, mir steht sich die Frage WiFi3 in den vorhandenen Sketch, mit einfügen?
Bitte, es hat mir Spaß gemacht.
Neee.....
Die Idee dahinter ist, dass die Anzahl der Pixel genutzt wird und Du keine magic Numbers verwendest.
Der Ansatz ist richtig, aber ;anim.pix*2; hat genau keine Wirkung.
Richtig muss es sein:
anim.idx = (anim.idx + 1) % (anim.pix * 2); // Schrittweise verschieben
