Taster betätigt, zeitversetzt LED schalten

Hallo Gemeinde,
Leider finde ich keine passende Lösung für mein Problem, da in den andere Beiträgen immer zwei mal gedrückt werden muss.

Mein Vorhaben:
Ein Taster wird gedrückt und bleibt gedrückt. (Eine Kugel liegt drauf) Arduino erkennt das der Taster seit 3 Sekunden gedrückt wird. Nun soll ein MOSFET angesteuert werden der dann ein Elektromagneten (Solenoid) für (ich sag mal) 1/2 sec. Ansteuert wird. Dadurch wird die Kugel vom Taste geschubst und das ganze kann von vorne beginnen.

Kann mir da einer helfen?:hugs:

Im englischen Teil des Forum müssen die Beiträge und Diskussionen in englischer Sprache verfasst werden.
Deswegen wurde diese Diskussion in den deutschen Teil des Forums verschoben.

mfg ein Moderator.

1 Like
#include <CombieTimer.h>
using namespace Combie::Timer;

const byte S   =  2; // Schalter Pin, negierend
const byte OUT = 13; // Ausgang

RisingEdgeTimer  ton(3000);    // steigende Flanke wird verzoegert
FallingEdgeTimer toff(500);    // abfallende Flanke wird verzoegert

void setup()
{
  pinMode(S, INPUT_PULLUP);
  pinMode(OUT, OUTPUT);
}

void loop()
{
  digitalWrite(OUT, toff(ton(!digitalRead(S))));
}

CombieLib.zip (388,3 KB)

1 Like

Hallo sirpsyko

Hier kommt ein Beispiel für mehrere Sacklöcher beim Flipper.
Die Anzahl und Verhalten dafür ist im Sketch konfigurierbar.

//make configuration here
constexpr uint8_t InputPins[] {A0, A1, A2};
constexpr uint8_t OutputPins[] {9, 10, 11};
constexpr uint32_t DelayTimes[] {3000, 3000, 3000};
constexpr uint32_t MosFetTimes[] {500, 500, 500};

und hier kommt der Sketch:

//https://forum.arduino.cc/t/taster-betatigt-zeitversetzt-led-schalten/1355850
//https://europe1.discourse-cdn.com/arduino/original/4X/7/e/0/7e0ee1e51f1df32e30893550c85f0dd33244fb0e.jpeg
#define ProjectName "Taster betätigt, zeitversetzt LED schalten"
#define NotesOnRelease "Arduino MEGA tested"
// -- some useful text replacements used because I'm lazy with typing --
#define toogle(x) digitalWrite (x,digitalRead(x)?LOW:HIGH)
#define CheckPin(pin) Serial.print ("test output@pin:"),Serial.print(pin),digitalWrite(pin,HIGH),delay(100),digitalWrite(pin,LOW),delay(100),Serial.println (" .. done")
// make names
enum TimerEvent {NotExpired, Expired};
enum TimerControl {Halt, Run};
enum ButtonState {Released, Pressed};
enum OffOn {Off, On};
// make variables
uint32_t currentMillis = millis();

//make configuration here
constexpr uint8_t InputPins[] {A0, A1, A2};
constexpr uint8_t OutputPins[] {9, 10, 11};
constexpr uint32_t DelayTimes[] {3000, 3000, 3000};
constexpr uint32_t MosFetTimes[] {500, 500, 500};
//-----------------------

// make structures
//-----------------------------------------
struct TIMER
{
  uint32_t interval;
  uint8_t control;
  uint32_t now;
  void start(uint32_t currentMillis)
  {
    now = currentMillis;
    control = Run;
  }
  void halt()
  {
    control = Halt;
  }
  uint8_t expired(uint32_t currentMillis)
  {
    uint8_t timerEvent = currentMillis - now >= interval and control;
    if (timerEvent == Expired) now = currentMillis;
    return timerEvent;
  }
};
struct BUTTONFED
{
  uint8_t led;
  uint8_t button;
  uint8_t stateOld;
  TIMER   debounce;
  TIMER   delayTime;
  TIMER   mosFetTime;

  void  make(uint8_t button_, uint8_t led_, uint32_t delayTime_, uint32_t mosFetTime_)
  {
    led  = led_;
    pinMode (led, OUTPUT);
    button = button_;
    pinMode (button, INPUT_PULLUP);
    stateOld = digitalRead(button) ? Off : On;
    debounce.control = Run;
    debounce.interval = 20;
    delayTime.control = Halt;
    delayTime.interval = delayTime_;
    mosFetTime.control = Halt;
    mosFetTime.interval =  mosFetTime_;
    CheckPin(led);
  }
  void run(uint32_t currentMillis)
  {
    if (debounce.expired(currentMillis) == Expired)
    {
      uint8_t stateNew = digitalRead(button) ? Off : On;
      if ( stateOld != stateNew)
      {
        stateOld = stateNew;
        (stateNew == Pressed) ? delayTime.start(currentMillis) : delayTime.halt();
      }
    }
    if (delayTime.expired(currentMillis) == Expired)
    {
      delayTime.halt();
      digitalWrite(led, On);
      mosFetTime.start(currentMillis);
    }
    if (mosFetTime.expired(currentMillis) == Expired)
    {
      mosFetTime.halt();
      digitalWrite(led, Off);
    }
  }
};
BUTTONFED buttonFeds[sizeof(InputPins)];
//-----------------------------------------
// make support
void heartBeat(const uint8_t LedPin, uint32_t currentMillis)
{
  static bool setUp = false;
  if (setUp == false) pinMode (LedPin, OUTPUT), setUp = true;
  digitalWrite(LedPin, (currentMillis / 500) % 2);
}
// make application
void setup()
{
  Serial.begin(115200);
  for (uint8_t n = 0; n < 32; n++) Serial.println("");
  Serial.print("Source: "), Serial.println(__FILE__);
  Serial.print(ProjectName), Serial.print(" - "), Serial.println(NotesOnRelease);
  uint8_t index = 0;
  for (auto &buttonFed : buttonFeds)
  {
    buttonFed.make(InputPins[index], OutputPins[index], DelayTimes[index] , MosFetTimes[index]) ;
    index ++;
  }
  delay(2000);
  Serial.println(" =-> and off we go\n");
}
void loop()
{
  currentMillis = millis();
  heartBeat(LED_BUILTIN, currentMillis);
  for (auto &buttonFed : buttonFeds) buttonFed.run(currentMillis);
}

Viel Spass beim Testen und Spielen :slight_smile:

Danke für eure Hilfe!
@combie ich bekomme deine Bibliothek nicht eingebunden. kommt immer Fehler 13....
@paulpaulson Programm läuft. Aber: wenn ich den Schalter gedrückt halte passiert nix. erst wenn ich ihn los lasse kommt nach 3000ms das 500ms Signal für den MOSFET. Ich wollte es aber gerne so haben das wenn di Kugel auf den Schalter fällt dieser gedrückt bleibt. hat er 3000ms ein gedrücktes Signal registriert soll er 500ms das Signal an den MOSFET schicken. Dieser steuert eine Magnetspule an die dann die Kugel von Schalter runter schiebt wodurch er dann öffnet und bereit für die nächste Kugel ist.

Die Taster sind mit INPUT_PULL konfiguriert und schalten GND auf den Eingangspin.

Hier die Inputpin-Beschaltung nach S3 erklärt.

1 Like

Ähm... schließt das nicht nahtlos an dieses Thema an? Warum dort nicht weiter machen?
Oder planst du für jede einzelne Funktion des Flippers einen eigenen Thread? :wink:

wie bindest du die Bibliothek ein? Über ".zip-bibliothek hinzufügen" klappt das nicht, weil sie bereits im "library"-Ordner mitgepackt vorliegen. Zieh dir einfach aus der zip-datei die benötigten libraries raus und schieb sie manuell in den installierten "libraries"-Ordner (../Dokumente/Arduino/libraries/..) hinein.

In der .zip Date findet sich ein libraries Ordner.
Und da drin meine Libs.

In deinem sketchbook Ordner findet sich ebenfalls ein libraries Ordner.

Mein Vorschlag:
Du nimmst die nötige Lib aus meinem libraries Ordner und wirfst sie in deinen sketchbook libraries Ordner

Und dann tuts das auch.

Danke der Hilfe.

Ja du hast recht das da auch geschaut wird. Da in dem Thema aber nicht jeder mit ließt, habe ich mir herausgenommen ein weiteren Thread auf zu machen. Sollte das hier nicht erwünscht sein, zügle ich mich in Zukunft. Tut mir leid!

Halt!

Der Fehler ist im anderen Thread passiert!

Thread hijacking:
Den anderen Thread hast du versucht feindlich zu übernehmen,
Das wird hier nicht geduldet.

Es ist also völlig OK einen eigenen/neuen Thread zu gründen,
Es ist der richtige Weg.

1 Like

Ich hatte erst gar nicht gecheckt, dass @sirpsyko nicht der Thread-Opener war. Und dann ging es wohl auch nicht schnell genug *g*. Sonst wäre der neu Thread wohl nicht aufgemacht worden.

@sirpsyko :
Hier ist der Code, der zu dem anderen Thread passt. Man muss keine zusätzliche Bibliothek einbinden.

Code
#include <Button_SL.hpp>   // https://github.com/DoImant/Button_SL

namespace gc {
constexpr uint8_t kickPin {3};
constexpr uint32_t kickDelay_ms {3000};
constexpr uint32_t kickDuration_ms {500};
}   // namespace gc

class Interval {
public:
  bool operator()(const uint32_t duration) {
    if (false == isStarted) { return start(false); }
    return (millis() - timeStamp >= duration) ? start(true) : false;
  }
  void reset() { isStarted = false; }

private:
  bool start(bool state = false) {
    isStarted = !state;   // Set the value to true on the first call
    timeStamp = millis();
    return state;
  }

private:
  bool isStarted {false};   // Flag = true if the first Operator() call has been made.
  uint32_t timeStamp {0};
};

class BallKicker {
public:
  BallKicker(Btn::Button& btn, uint8_t pin, uint32_t kDelay_ms, uint32_t kDuration_ms)
      : btn {btn}, pin {pin}, kDelay_ms {kDelay_ms}, kDuration_ms {kDuration_ms} {}
  void begin() { pinMode(pin, OUTPUT); }
  void check() { isKickerActive ? kicker() : checkPin(); };

private:
  void checkPin() {
    if (!btn.tick()) {
      timer.reset();
    } else if (timer(kDelay_ms)) {
      digitalWrite(pin, HIGH);
      isKickerActive = true;
    }
  }
  void kicker() {
    if (timer(kDuration_ms)) {
      digitalWrite(pin, LOW);
      isKickerActive = false;
    }
  }

private:
  Btn::Button& btn;
  uint8_t pin;
  uint32_t kDelay_ms;
  uint32_t kDuration_ms;
  Interval timer;
  bool isKickerActive {false};
};

Interval interval;
Btn::Button btnKicker {2};
BallKicker bKicker {btnKicker, gc::kickPin, gc::kickDelay_ms, gc::kickDuration_ms};

void setup() {
  Serial.begin(115200);
  btnKicker.begin();
  bKicker.begin();
}

void loop() { bKicker.check(); }

Zum Ausprobieren Score-Counter Code um den neuen Schalter ergänzt. Der rote Button ist der Schalter für die Kugel. Die LED simuliert den Magnetschalter.

1 Like

Danke erstmal für deinen Beitrag. Mega Sache :heart_eyes:

Ich hoffe es ist okay wenn wir hier weiter machen?! Scheinbar soll man für seine eigenen Projekte ja nen eigenen Thread machen und sich nicht anderweitig einzecken…

Ich setze mich morgen mal dran und teste das Ganze :hugs:betonter Text

Da musst Du mich nicht fragen. Mir ist das wurscht.

1 Like

Die Regeln sind in der Sache recht eindeutig.

Meine Antwort bezog sich darauf, ob er hier weitermacht oder nicht ... Das mit Kapern wurde ja bereits in #10 erklärt.

Ach so..
Aus meiner Sicht ist das klar, hier weiter machen, nicht weiter kapern.

Ja, meine Antwort war missverständlich. Aber ich denke, jetzt ist das geklärt.

Auf dem Beschaltungs-Plan wird von der 3. Lösung (mit S1) abgeraten. Ich verstehe aber nicht, warum. Es dürfte doch egal sein, ein Pullup und nach GND schalten, oder ein Pulldown und nach 5V schalten. Im Sketch dann entsprechend anders abfragen.

Ein anderes Problem, was mir durch den Kopf geht ist: Was passiert, wenn man versehentlich diesen Pin als Ausgang deklariert, was ja schnell mal passieren kann. Dann gäbe es bei Knopfdruck direkte Verbindung zu GND bzw. 5V.

1 Like

Man sollte wissen was man macht :wink:

Arduinos arbeiten intern mit Rauch!
Wenn der einmal ausgetreten ist, dann sind sie meist kaputt.
(AVR sind da recht zäh)

Wenn der interne Pullup ausreicht, warum dann nicht verwenden?
Ansonsten: Mach, wie du es für richtig hältst.

ps:
Ich sehe auch nicht was in #6 an S3 falsch/bad sein soll