IF-Bedingung unterbrechen, bis ein Reed-Schalter geöffnet und wieder geschlossen wird. Hier für ein Projekt einer automatischen Füllstation

Wenn ich doch nur einen Bruchteil deines Wissens hätte, dann fühle ich mich nicht immer wieder wie ein Anfänger. Gut Ding will Weile haben. Aber erstmal ein großes Danke.

Ich habe deinen Sketch mal aufgespielt und getestet. Im Prinzip funktioniert er soweit, aber noch nicht ganz rund. Ich denke es hängt mit dem Mikroschalter (homePin) und dem Button (prefillPin) zusammen. Kann es noch nicht ganz durchblicken.

Nach dem Einschalten soll sich der Stepper selbst mit dem Mikroschalter nullen. Das macht er nicht mehr. Startet, wo er gerade steht. Könnte ich mit meinem ursprünglichen Code sicher lösen.

Wenn ich einen beliebigen Reedschalter schließe, also ein Glas auf eine der Positionen stelle, dann fährt er die Position an, füllt und lässt die LED blinken. (Übrigens eine schöne Idee von dir.) Danach passiert wunschgemäß nichts mehr, bis ich den Kontakt öffne. Also schonmal richtig. Perfekt.

Stelle ich aber nun mehr als ein Glas auf, dann arbeitet er das "erste" ab und wartet. Ich muss erst alle herunternehmen und immer eins nach dem anderen. In meinem Wunschdenken möchte ich einfach alles Gläser aufstellen. Werden abgearbeitet und dann geht´s weiter.

Schätze hier mit den Abfragen der Pin´s oder im folgenden mit dem Button

Ich hänge mal ein Bild mit rein, wie mein Prototyp auf dem "Papier" aussieht.

AAAHHHH!
Und ich hab mich gefragt, was das ist.
Ok, baue ich noch. Dann ist auch meine Nullpunkt-Editierfunktion natürlich überflüssig, aber ich fand die schick :wink:

Bekommen wir hin.
Da muss ich nur einen Merker bauen, dass das Glas nicht abgefragt wird, bis es entnommen wurde.
Das bringt dann natürlich den Ablauf etwas in Schwierigkeiten, da ich während des Füllvorganges die anderen Reed nicht abfrage.
Macht nix, schreibe ich komplett um.

:grin:

:smile: Hab mich vielleicht auch ein wenig umständlich ausgedrückt und das Bild zu Beginn wäre wohl sinnvoll gewesen. Ist auch schick geworden.

Ich hab mal was gebaut.
Ich hoffe es ausreichend kommentiert zu haben.
Mit den LED bin ich noch nciht durch.
In back2Home() musst Du sehen, ob ich richtig rum drehe. Wenn falsch, muss da rechts reingeschrieben werden :slight_smile:

Die Logik ist so gemacht, dass immer alle shots abgefragt werden und nicht nach einem Durchlauf wieder von vorn angefangen wird.
Sonst würden bei einem dauerndem Neuaufstellen von #1 und #2 die Shots dahinter nie voll werden :slight_smile:
Mit den LED's bin ich noch nciht ganz durch - aber mach mal erstmal, dass ich grobe Fehler ausschliessen kann.

/*
  Stepper filling station by m0rph3xx https://forum.arduino.cc/u/morphexx/summary
  Driver DRV8825 from AZ
*/

//----------------------------------------------------------------------------------------------------------------------

//STEPPER AND home_switch (MICROSWITCH)
#include <AccelStepper.h>  // Library created by Mike McCauley at http://www.airspayce.com/mikem/arduino/AccelStepper/

// Define a stepper and the pins it will use
#define dir_pin 2   // Pin 2 connected to DIR pin
#define step_pin 3  // Pin 3 connected to STP pin
// #define MS0 x        // Pin x connected to MS1 pin
// #define MS1 x        // Pin x connected to MS1 pin
// #define MS2 x        // Pin x connected to MS2 pin
// #define SLEEP_PIN x  // Pin x connected to SLEEP pin

AccelStepper stepper(AccelStepper::DRIVER, step_pin, dir_pin);  // Pin 2 connected to STP pin and Pin 3 connected to DIR pin of AZ DRV8825

//  pinMode(MS0, OUTPUT);
//  pinMode(MS1, OUTPUT);
//  pinMode(MS2, OUTPUT);
//  pinMode(SLEEP_PIN, OUTPUT);
//  digitalWrite(SLEEP_PIN, HIGH);  // Wake up AZ DRV8825
//  delay(5);  // Wait for stepper driver wake up
/* Configure type of Steps on AZ DRV8825:

  The DRV8825 driver has three input pins for microstep resolutions:
  " MS0
  " MS1
  " MS2

  By setting appropriate logic levels on these pins, we can set the drive mode of the motor to
  drive mode of the motor to one of these six modes:

  M0    M1    M2    Microstep resolution
  LOW   LOW   LOW   Full step
  HIGH  LOW   LOW   Half step
  LOW   HIGH  LOW   Quarter step
  HIGH  HIGH  LOW   Eighth step
  LOW   LOW   HIGH  Sixteenth step
  HIGH  LOW   HIGH  1/32 step
  LOW   HIGH  HIGH  1/32 step
  HIGH  HIGH  HIGH  1/32 step

  Remember that you don’t need to manually set the pins
  The DRV8825 handles this configuration for you based on the desired mode.

  Alternatively, to save pins on the microcontroller, the modes can be permanently connected
  via the 5V pin of the controller (5V=HIGH, disconnected=LOW).
*/
//  digitalWrite(MS0, LOW);  // Configures to Full Steps
//  digitalWrite(MS1, LOW);  // Configures to Full Steps
//  digitalWrite(MS2, LOW);  // Configures to Full Steps
//----------------------------------------------------------------------------------------------------------------------
// Start homing of stepper motor at startup



// REED SWITCH AND LED and Number of steps for positions 1-5 of the shot glasses
constexpr byte shotNums {5};
struct SHOT
{
  const byte switchPin;
  const byte ledPin;
  const uint16_t position;
  bool isFilled;
  uint32_t ledTime;
} shot[shotNums] =
{
  {4, A1,  90, false, 0,},
  {5, A2, 165, false, 0,},
  {6, A3, 240, false, 0,},
  {7, A4, 315, false, 0,},
  {8, A5, 395, false, 0,},
};

//RELAY AND BUTTON

constexpr byte relayPin {9};      //PIN RELAY - Relay requires 5V
constexpr byte homePin {10};      //PIN home switch (MICROSWITCH) - Pin to set home/zero for stepper after startup/reset
constexpr byte prefillPin {11};   //PIN BUTTON GREY - Pin to activate the relay (pump) manually via grey foil button

constexpr bool relayOn{ HIGH };       // Turn relay on
constexpr bool relayOff{ !relayOn };  // Turn relay off

constexpr bool ledOn{ HIGH };
constexpr bool ledOff{ !ledOn };

constexpr bool rechts { LOW };
constexpr bool links (!rechts);

enum class STATUS   // was es alles so gibt
{
  waitToStart,      // Tastenabfrage für Start
  prefill,          //
  fillpos,
  filling,
  fillend,
  homepos,
  backtohome,
};

STATUS status = STATUS::waitToStart;
byte activeShot;
uint32_t switchTime;

constexpr uint32_t fillTime {2000};
constexpr uint32_t pauseTime {800};
//----------------------------------------------------------------------------------------------------------------------
void setup()
{
  //STEPPER HOMING WITH home_switch - Enable internal pull-up for the switch
  pinMode(homePin, INPUT_PULLUP);
  pinMode(dir_pin, OUTPUT);
  pinMode(step_pin, OUTPUT);
  stepper.setMaxSpeed(1000);      // Set Maximum Speed of stepper (Slower to get better accuracy)
  stepper.setAcceleration(400);   // Set acceleration of stepper
  //----------------------------------------------------------------------------------------------------------------------
  // REED SWITCH AND LED - Enable internal pull-up for the reed switch - LED on
  for (byte b = 0; b < shotNums; b++)
  {
    pinMode(shot[b].switchPin, INPUT_PULLUP);
    pinMode(shot[b].ledPin, OUTPUT);
    digitalWrite(shot[b].ledPin, ledOn);
  }
  //----------------------------------------------------------------------------------------------------------------------
  // RELAY AND BUTTON - Enable internal pull-up for the button
  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, relayOff);  // Turn the RELAY off
  pinMode(prefillPin, INPUT_PULLUP);
  //
  back2Home(); // -> Ausgangsstellung und Nullpunkt setzen
}

void loop()
{
  fillingGlass();
}

void fillingGlass()
{
  switch (status)
  {
    case STATUS::waitToStart:                               // Ausgangslage
      allLed(ledOn);                                        //
      if (!digitalRead(prefillPin))                         //
      {
        allLed(ledOff);                                     // Visualisierung
        // nur in der Ausgangslage prefill ausführbar!
        if (back2Home)                                      // fährt gesichert in Ausgangsposition
        {
          digitalWrite(relayPin, relayOn);                  //
          status = STATUS::prefill;
        }
      }
      else                                                  // Nicht im Vorfüllmodus
      {
        if (!digitalRead(shot[activeShot].switchPin))       // Glas steht drauf
        {
          if (!shot[activeShot].isFilled)                   // Es ist nicht besetzt
          {
            stepper.moveTo(shot[activeShot].position);      // aktivieren
            shot[activeShot].isFilled = true;               // Merken
            switchTime = millis();                          // Startzeit
            status = STATUS::fillpos;                       // Nächsten Schritt
          }
        }
        else                                                // Glas steht nicht drauf
        {
          shot[activeShot].isFilled = true;                 // Merker löschen
          if (++activeShot >= shotNums)                     // Nächstes Glas auswählen
          { activeShot = 0; }                               // Am Ende von vorn anfangen
        }
      }
      break;
    case STATUS::prefill:
      for (byte b = 0; b < shotNums; b++)                   // Visualisierung
      { ledBlink(150); }
      if (digitalRead(prefillPin))                          // Button nicht gedrückt
      {
        digitalWrite(relayPin, relayOff);                   //
        status = STATUS::waitToStart;                       // gehe in Ausgangszustand
      }
      break;
    case STATUS::fillpos:
      ledBlink(70);                                         // Kleines blinkblink AuslaufLED
      if (millis() - switchTime > pauseTime)                // Zwangspause abgelaufen
      {
        digitalWrite(relayPin, relayOn);                    // Füllen starten
        switchTime = millis();                              // Startzeit merken
        status = STATUS::filling;                           // Nächster Schritt
      }
      break;
    case STATUS::filling:
      ledBlink(200);                                        // blinkblink
      if (millis() - switchTime > fillTime)                 // Füllzeit abgelaufen
      { status = STATUS::fillend; }                         // Nächster Schritt
      break;
    case STATUS::fillend:
      digitalWrite(shot[activeShot].ledPin, ledOff);        // gesichert AUS machen
      digitalWrite(relayPin, relayOff);                     // Füllen beenden
      switchTime = millis();                                // merken
      status = STATUS::homepos;                             // nächster Schritt
      break;
    case STATUS::homepos:
      if (millis() - switchTime > pauseTime)                // Zwangspause -> es tropft nach!
      { status = STATUS::waitToStart; }                     // fange an von Vorn
      break;
  }
  stepper.runToPosition();
}

//
bool back2Home()                                            //
{
  while (digitalRead(homePin))                              // Microtaster nicht ausgelöst
  {
    digitalWrite(dir_pin, links);                           // Stepper Richtung Endanschlag
    oneStep();                                              // Ein Stück fahren
    stepper.setCurrentPosition(0);                          // Null- / Startpunkt
  }
  stepper.moveTo(0);                                        // Feststellen
  return !digitalRead(homePin);                             // gibt true zurück, wenn ausgelöst
}
//
void oneStep()                                              // Macht wie es heisst
{
  digitalWrite(step_pin, HIGH);
  delay(5);
  digitalWrite(step_pin, LOW);
  delay(5);
}
//
void ledBlink(const byte blinkTime)         //
{
  if (millis() - shot[activeShot].ledTime > blinkTime)
  {
    digitalWrite(shot[activeShot].ledPin, !digitalRead(shot[activeShot].ledPin));
    shot[activeShot].ledTime = millis();
  }
}
//
void allLed(bool state)
{
  for (byte b = 0; b < shotNums; b++)
  {
    digitalWrite(shot[b].ledPin, state);
    shot[b].ledTime = millis();
  }
}

Für mich ein Mega Konstrukt. :exploding_head:

Du gibst dir richtig viel Mühe, um mir das verständlich zu zeigen.

Musste ich umschreiben, aber mit meinem "Fachwissen" kein Thema. :laughing:

Ich habe es mal ausprobiert und schon wieder einen riesen Schritt weiter mit deinem Code. Doch an irgendeiner Stelle hängt es leider noch, obwohl du dir schon für mich den Kopf zerbrichst. Kann ich glaube niemals wieder gut machen. :grimacing:

Folgendes passiert, oder nicht. Es gibt zwei Unterschiedliche Szenarien, aber in beiden Fällen fährt der Stepper nach dem einschalten sein back2Home() an, bleibt dort auch stehen und alle LED schalten sich ein. Prefill funktioniert ebenso.

#1 Wenn ich vor dem einschalten keinen shot aufstelle, dann fährt den Stepper zwar seinen Homepunkt an, aber die Reed´s werden ignoriert. Egal, ob ich dann Einen oder Mehrere shots aufgestelle.

#2 Wenn ich vor dem einschalten jedoch Einen, Zwei oder auch alle Shots aufstelle, dann fährt der Stepper seinen Homepunkt an und läuft zur ersten aufgestellten Position, füllt und Stepper bleibt auf der Position stehen. Sobald ich dann diesen gefüllten shot runternehme fährt er zur nächsten Position, usw., bis er an der letzten Position angelangt ist. Dort verweilt der Stepper. Sprich er fährt nach dem Füllvorgang nicht auf seinen Homepunkt zurück, sondern bleibt dort stehen. Auch nicht, wenn ich das letzte Glas runternehme.

Was nun noch hinzukommt, das keine weitere Abfrage der shots stattfindet. Nach dieser ersten Füllrunde ist Schluß. Nach Aus- und Wiedereinschalten geht es mit den jeweils aufgestellten Shots wieder von vorn los. Stelle ich z.B. 3 Gläser zum Start auf, dann werden nur diese 3 beachtet und der Stepper bleibt dort beim letzten stehen. Stelle ich alle auf, dann werden auch alle befüllt.

Im Prinzip läuft das ab, was passieren soll. Gläser vor dem einschalten aufzustellen, um eine Grundabfrage vorzugeben ist in Ordnung. Die LED blinken auch während des Füllvorgangs.

Denke das ist der Punkt, das er am "Ende" aufhört.

Ich schau mal rauf, dass sollte nicht so sein.#
Evtl. zähle ich zu weit und komm dann nicht mehr richtig zurück.
Ich mach das ja blind... :slight_smile:

Danke dir. Genial, das du blind weiterkommst, als ich sehend. :laughing:

1 Like

Ich hab was.
Zeile 172 - Da wird der merker gelöscht.
Muss richtig sein:

          shot[activeShot].isFilled = false;                // Merker löschen

Ok, das muss in einer etwas größeren Operation gemacht werden, da ja erstmal sichergestellt werden muss, dass nicht irgendwo ein Glas draufgestellt ist.
(Wird ein Merker und ein Zähler - also nix besnderes)
In dem Zusammenhang schau ich mir dann auch die Gesamtlogik nochmal an.

Das kleine Wörtchen false hat die ersten Probleme direkt gelöst.
Nun ist es egal, ob Gläser vor oder nach dem einschalten aufgestellt sind. Sie werden nacheinander angefahren. Wenn das gefüllte Glas heruntergenommen wird, geht´s zum Nächsten.

Jetzt muss "nur" noch der Stepper nach der Befüllung zum Home zurückkehren und nicht mit der Befüllung des nächsten Glases warten, bis das Vorherige runtergenommen wurde.

Hatte ich gestern schon angefngen mir eine Strategie zu bauen, aber liegt noch auf Halde - War anderweitig im Einsatz.
Kommt zu morgen.

Du musst dich doch nicht für Wartezeiten entschuldigen. Ist doch alles freiwillig von dir und keine Verpflichtung. Bitte bloß keinen Stress.

Ich hab mal was versucht - mit einem einzigen Bit. :sweat_smile:
Wenns funktioniert gut, wenn nicht auch gut, dann war ich auf Abwegen.
Evtl. musst Du noch die Richtungen anpassen, da ich ja meinen Stand weiterverwendet habe.

/*
  Stepper filling station by m0rph3xx https://forum.arduino.cc/u/morphexx/summary
  Driver DRV8825 from AZ
*/

//----------------------------------------------------------------------------------------------------------------------

//STEPPER AND home_switch (MICROSWITCH)
#include <AccelStepper.h>  // Library created by Mike McCauley at http://www.airspayce.com/mikem/arduino/AccelStepper/

// Define a stepper and the pins it will use
#define dir_pin 2   // Pin 2 connected to DIR pin
#define step_pin 3  // Pin 3 connected to STP pin
// #define MS0 x        // Pin x connected to MS1 pin
// #define MS1 x        // Pin x connected to MS1 pin
// #define MS2 x        // Pin x connected to MS2 pin
// #define SLEEP_PIN x  // Pin x connected to SLEEP pin

AccelStepper stepper(AccelStepper::DRIVER, step_pin, dir_pin);  // Pin 2 connected to STP pin and Pin 3 connected to DIR pin of AZ DRV8825

//  pinMode(MS0, OUTPUT);
//  pinMode(MS1, OUTPUT);
//  pinMode(MS2, OUTPUT);
//  pinMode(SLEEP_PIN, OUTPUT);
//  digitalWrite(SLEEP_PIN, HIGH);  // Wake up AZ DRV8825
//  delay(5);  // Wait for stepper driver wake up
/* Configure type of Steps on AZ DRV8825:

  The DRV8825 driver has three input pins for microstep resolutions:
  " MS0
  " MS1
  " MS2

  By setting appropriate logic levels on these pins, we can set the drive mode of the motor to
  drive mode of the motor to one of these six modes:

  M0    M1    M2    Microstep resolution
  LOW   LOW   LOW   Full step
  HIGH  LOW   LOW   Half step
  LOW   HIGH  LOW   Quarter step
  HIGH  HIGH  LOW   Eighth step
  LOW   LOW   HIGH  Sixteenth step
  HIGH  LOW   HIGH  1/32 step
  LOW   HIGH  HIGH  1/32 step
  HIGH  HIGH  HIGH  1/32 step

  Remember that you don’t need to manually set the pins
  The DRV8825 handles this configuration for you based on the desired mode.

  Alternatively, to save pins on the microcontroller, the modes can be permanently connected
  via the 5V pin of the controller (5V=HIGH, disconnected=LOW).
*/
//  digitalWrite(MS0, LOW);  // Configures to Full Steps
//  digitalWrite(MS1, LOW);  // Configures to Full Steps
//  digitalWrite(MS2, LOW);  // Configures to Full Steps
//----------------------------------------------------------------------------------------------------------------------
// Start homing of stepper motor at startup



// REED SWITCH AND LED and Number of steps for positions 1-5 of the shot glasses
constexpr byte shotNums {5};
struct SHOT
{
  const byte switchPin;
  const byte ledPin;
  const uint16_t position;
  bool isFilled;
  uint32_t ledTime;
} shot[shotNums] =
{
  {4, A1,  90, false, 0,},
  {5, A2, 165, false, 0,},
  {6, A3, 240, false, 0,},
  {7, A4, 315, false, 0,},
  {8, A5, 395, false, 0,},
};

//RELAY AND BUTTON

constexpr byte relayPin {9};      //PIN RELAY - Relay requires 5V
constexpr byte homePin {10};      //PIN home switch (MICROSWITCH) - Pin to set home/zero for stepper after startup/reset
constexpr byte prefillPin {11};   //PIN BUTTON GREY - Pin to activate the relay (pump) manually via grey foil button

constexpr bool relayOn{ HIGH };       // Turn relay on
constexpr bool relayOff{ !relayOn };  // Turn relay off

constexpr bool ledOn{ HIGH };
constexpr bool ledOff{ !ledOn };

constexpr bool rechts { LOW };
constexpr bool links (!rechts);

enum class STATUS   // was es alles so gibt
{
  waitToStart,      // Tastenabfrage für Start
  prefill,          //
  fillpos,
  filling,
  fillend,
  homepos,
};

STATUS status = STATUS::waitToStart;
byte activeShot;
byte lastFillingShot = 0xFF;
uint32_t switchTime;
bool isFill;

constexpr uint32_t fillTime {2000};
constexpr uint32_t pauseTime {800};
//----------------------------------------------------------------------------------------------------------------------
void setup()
{
  //STEPPER HOMING WITH home_switch - Enable internal pull-up for the switch
  pinMode(homePin, INPUT_PULLUP);
  pinMode(dir_pin, OUTPUT);
  pinMode(step_pin, OUTPUT);
  stepper.setMaxSpeed(1000);      // Set Maximum Speed of stepper (Slower to get better accuracy)
  stepper.setAcceleration(400);   // Set acceleration of stepper
  //----------------------------------------------------------------------------------------------------------------------
  // REED SWITCH AND LED - Enable internal pull-up for the reed switch - LED on
  for (byte b = 0; b < shotNums; b++)
  {
    pinMode(shot[b].switchPin, INPUT_PULLUP);
    pinMode(shot[b].ledPin, OUTPUT);
    digitalWrite(shot[b].ledPin, ledOn);
  }
  //----------------------------------------------------------------------------------------------------------------------
  // RELAY AND BUTTON - Enable internal pull-up for the button
  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, relayOff);  // Turn the RELAY off
  pinMode(prefillPin, INPUT_PULLUP);
  //
  back2Home(); // -> Ausgangsstellung und Nullpunkt setzen
}

void loop()
{
  fillingGlass();
}

void fillingGlass()
{
  switch (status)
  {
    case STATUS::waitToStart:                               // Ausgangslage
      allLed(ledOn);                                        //
      if (!digitalRead(prefillPin))                         //
      {
        allLed(ledOff);                                     // Visualisierung
        // nur in der Ausgangslage prefill ausführbar!
        if (back2Home())                                      // fährt gesichert in Ausgangsposition
        {
          digitalWrite(relayPin, relayOn);                  //
          status = STATUS::prefill;
        }
      }
      else                                                  // Nicht im Vorfüllmodus
      {
        if (!digitalRead(shot[activeShot].switchPin))       // Glas steht drauf
        {
          if (!shot[activeShot].isFilled)                   // Es ist nicht besetzt
          {
            stepper.moveTo(shot[activeShot].position);      // aktivieren
            shot[activeShot].isFilled = true;               // Merken
            switchTime = millis();                          // Startzeit
            isFill = true;
            status = STATUS::fillpos;                       // Nächsten Schritt
          }
        }
        else                                                // Glas steht nicht drauf
        {
          shot[activeShot].isFilled = false;                // Merker löschen
          if (++activeShot >= shotNums)                     // Nächstes Glas auswählen
          {
            activeShot = 0;
            if (!isFill)
            {back2Home();}
            isFill = false;
          }                               // Am Ende von vorn anfangen
        }
      }
      break;
    case STATUS::prefill:
      for (byte b = 0; b < shotNums; b++)                   // Visualisierung
      { ledBlink(150); }
      if (digitalRead(prefillPin))                          // Button nicht gedrückt
      {
        digitalWrite(relayPin, relayOff);                   //
        status = STATUS::waitToStart;                       // gehe in Ausgangszustand
      }
      break;
    case STATUS::fillpos:
      ledBlink(70);                                         // Kleines blinkblink AuslaufLED
      if (millis() - switchTime > pauseTime)                // Zwangspause abgelaufen
      {
        digitalWrite(relayPin, relayOn);                    // Füllen starten
        switchTime = millis();                              // Startzeit merken
        status = STATUS::filling;                           // Nächster Schritt
      }
      break;
    case STATUS::filling:
      ledBlink(200);                                        // blinkblink
      if (millis() - switchTime > fillTime)                 // Füllzeit abgelaufen
      { status = STATUS::fillend; }                         // Nächster Schritt
      break;
    case STATUS::fillend:
      digitalWrite(shot[activeShot].ledPin, ledOff);        // gesichert AUS machen
      digitalWrite(relayPin, relayOff);                     // Füllen beenden
      switchTime = millis();                                // merken
      status = STATUS::homepos;                             // nächster Schritt
      break;
    case STATUS::homepos:
      if (millis() - switchTime > pauseTime)                // Zwangspause -> es tropft nach!
      { status = STATUS::waitToStart; }                     // fange an von Vorn
      break;
  }
  stepper.runToPosition();
}

//
bool back2Home()                                            //
{
  while (digitalRead(homePin))                              // Microtaster nicht ausgelöst
  {
    digitalWrite(dir_pin, links);                           // Stepper Richtung Endanschlag
    oneStep();                                              // Ein Stück fahren
    stepper.setCurrentPosition(0);                          // Null- / Startpunkt
  }
  stepper.moveTo(0);                                        // Feststellen
  return !digitalRead(homePin);                             // gibt true zurück, wenn ausgelöst
}
//
void oneStep()                                              // Macht wie es heisst
{
  digitalWrite(step_pin, HIGH);
  delay(5);
  digitalWrite(step_pin, LOW);
  delay(5);
}
//
void ledBlink(const byte blinkTime)         //
{
  if (millis() - shot[activeShot].ledTime > blinkTime)
  {
    digitalWrite(shot[activeShot].ledPin, !digitalRead(shot[activeShot].ledPin));
    shot[activeShot].ledTime = millis();
  }
}
//
void allLed(bool state)
{
  for (byte b = 0; b < shotNums; b++)
  {
    digitalWrite(shot[b].ledPin, state);
    shot[b].ledTime = millis();
  }
}
1 Like

Test ist erfolgreich abgeschlossen. :slight_smile:

Es funktioniert fehlerfrei und zu 99,99% wie erhofft! :+1:

Hast du sauber lösen können. Bis ich es allein hinbekommen hätte, dann würde wahrscheinlich auf meinem Grabstein stehen: "Er versucht es immer noch..." :laughing:

Vielen, vielen Dank für deine bisherige Hilfe.

Ich würde dich gerne noch mit den letzten 0,01%, der Sahnehaube, herausfordern. Bin aber bisher mehr als zufrieden und könnte mit dieser Lösung mit einem ruhigen gewissen Leben. :wink:

Die Abfrage der Reeds (Gläser) erfolgt kontinuierlich und ich kann jederzeit Gläser herunternehmen und draufstellen. Egal, ob vor, oder nach dem Einschalten. Die Sahnehaube würde jetzt folgendes sein.

Jetzt:

  1. Glas aufstellen
  2. Stepper fährt zur Position
  3. Glas wird befüllt
  4. Stepper wartet an der Position bis das Glas heruntergenommen wurde
  5. Stepper fährt zum nächsten Glas oder zum Home zurück

Sahnehaube:

  1. Glas aufstellen
  2. Stepper fährt zur Position
  3. Glas wird befüllt
    4. Stepper wartet an der Position bis das Glas heruntergenommen wurde
  4. Stepper fährt zum Nächsten Glas oder zum Home zurück

Also kurz zusammengefasst: Direkt ohne die "Nutzereingabe" zum nächsten Glas oder Home

Wenn du noch Lust zum tüfteln hast, dann tobe dich gern aus. Bisher der absolute Hammer!

1 Like

da ich morgen den tag über nicht da bin, versuche ich es zu frühmorgens noch zu bauen. (hab hier grad noch nen anderen Patienten :wink: ) und dann schaun wa mal.
Um die Zeit solltest es dann getestet haben, damit ich nen Feedback habe.

Oh du bist der Beste.

Aber ich wiederhole es sehr gern und auch immer wieder. Bitte keine Hektik. Hängt kein Leben von ab. :wink:

Ej, ohne Nachschub wirds trocken im Getriebe :slight_smile:
Ich hab mal was angefangen.
Für das Checken und zurückfahren eine eigene Funktion gebaut - die kann dann zu jeder Zeit aufgerufen werden.
Nu hab ich die hoffentlich auch an die richtige Stelle gesetzt.
Es ist wiede kommentiert.
Wenn Fragen sind, frag.

/*
  Stepper filling station by m0rph3xx https://forum.arduino.cc/u/morphexx/summary
  Driver DRV8825 from AZ
*/

//----------------------------------------------------------------------------------------------------------------------

//STEPPER AND home_switch (MICROSWITCH)
#include <AccelStepper.h>  // Library created by Mike McCauley at http://www.airspayce.com/mikem/arduino/AccelStepper/

// Define a stepper and the pins it will use
#define dir_pin 2   // Pin 2 connected to DIR pin
#define step_pin 3  // Pin 3 connected to STP pin
// #define MS0 x        // Pin x connected to MS1 pin
// #define MS1 x        // Pin x connected to MS1 pin
// #define MS2 x        // Pin x connected to MS2 pin
// #define SLEEP_PIN x  // Pin x connected to SLEEP pin

AccelStepper stepper(AccelStepper::DRIVER, step_pin, dir_pin);  // Pin 2 connected to STP pin and Pin 3 connected to DIR pin of AZ DRV8825

//  pinMode(MS0, OUTPUT);
//  pinMode(MS1, OUTPUT);
//  pinMode(MS2, OUTPUT);
//  pinMode(SLEEP_PIN, OUTPUT);
//  digitalWrite(SLEEP_PIN, HIGH);  // Wake up AZ DRV8825
//  delay(5);  // Wait for stepper driver wake up
/* Configure type of Steps on AZ DRV8825:

  The DRV8825 driver has three input pins for microstep resolutions:
  " MS0
  " MS1
  " MS2

  By setting appropriate logic levels on these pins, we can set the drive mode of the motor to
  drive mode of the motor to one of these six modes:

  M0    M1    M2    Microstep resolution
  LOW   LOW   LOW   Full step
  HIGH  LOW   LOW   Half step
  LOW   HIGH  LOW   Quarter step
  HIGH  HIGH  LOW   Eighth step
  LOW   LOW   HIGH  Sixteenth step
  HIGH  LOW   HIGH  1/32 step
  LOW   HIGH  HIGH  1/32 step
  HIGH  HIGH  HIGH  1/32 step

  Remember that you don’t need to manually set the pins
  The DRV8825 handles this configuration for you based on the desired mode.

  Alternatively, to save pins on the microcontroller, the modes can be permanently connected
  via the 5V pin of the controller (5V=HIGH, disconnected=LOW).
*/
//  digitalWrite(MS0, LOW);  // Configures to Full Steps
//  digitalWrite(MS1, LOW);  // Configures to Full Steps
//  digitalWrite(MS2, LOW);  // Configures to Full Steps
//----------------------------------------------------------------------------------------------------------------------
// Start homing of stepper motor at startup



// REED SWITCH AND LED and Number of steps for positions 1-5 of the shot glasses
constexpr byte shotNums {5};
struct SHOT
{
  const byte switchPin;
  const byte ledPin;
  const uint16_t position;
  bool isFilled;
  uint32_t ledTime;
} shot[shotNums] =
{
  {4, A1,  90, false, 0,},
  {5, A2, 165, false, 0,},
  {6, A3, 240, false, 0,},
  {7, A4, 315, false, 0,},
  {8, A5, 395, false, 0,},
};

//RELAY AND BUTTON

constexpr byte relayPin {9};      //PIN RELAY - Relay requires 5V
constexpr byte homePin {10};      //PIN home switch (MICROSWITCH) - Pin to set home/zero for stepper after startup/reset
constexpr byte prefillPin {11};   //PIN BUTTON GREY - Pin to activate the relay (pump) manually via grey foil button

constexpr bool relayOn{ HIGH };       // Turn relay on
constexpr bool relayOff{ !relayOn };  // Turn relay off

constexpr bool ledOn{ HIGH };
constexpr bool ledOff{ !ledOn };

constexpr bool rechts { LOW };
constexpr bool links (!rechts);

enum class STATUS   // was es alles so gibt
{
  waitToStart,      // Tastenabfrage für Start
  prefill,          //
  fillpos,
  filling,
  fillend,
  homepos,
};

STATUS status = STATUS::waitToStart;
byte activeShot;
uint32_t switchTime;

constexpr uint32_t fillTime {2000};
constexpr uint32_t pauseTime {800};
//----------------------------------------------------------------------------------------------------------------------
void setup()
{
  //STEPPER HOMING WITH home_switch - Enable internal pull-up for the switch
  pinMode(homePin, INPUT_PULLUP);
  pinMode(dir_pin, OUTPUT);
  pinMode(step_pin, OUTPUT);
  stepper.setMaxSpeed(1000);      // Set Maximum Speed of stepper (Slower to get better accuracy)
  stepper.setAcceleration(400);   // Set acceleration of stepper
  //----------------------------------------------------------------------------------------------------------------------
  // REED SWITCH AND LED - Enable internal pull-up for the reed switch - LED on
  for (byte b = 0; b < shotNums; b++)
  {
    pinMode(shot[b].switchPin, INPUT_PULLUP);
    pinMode(shot[b].ledPin, OUTPUT);
    digitalWrite(shot[b].ledPin, ledOn);
  }
  //----------------------------------------------------------------------------------------------------------------------
  // RELAY AND BUTTON - Enable internal pull-up for the button
  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, relayOff);  // Turn the RELAY off
  pinMode(prefillPin, INPUT_PULLUP);
  //
  back2Home(); // -> Ausgangsstellung und Nullpunkt setzen
}

void loop()
{
  fillingGlass();
}

void fillingGlass()
{
  switch (status)
  {
    case STATUS::waitToStart:                               // Ausgangslage
      if (!digitalRead(prefillPin))                         // Vorrangschaltung
      { status = STATUS::prefill; }
      else                                                  // Kein prefill
      {
        if (!digitalRead(shot[activeShot].switchPin))       // Glas steht drauf
        {
          digitalWrite(shot[activeShot].ledPin, ledOff);    // Visualisieren
          if (!shot[activeShot].isFilled)                   // Es ist nicht besetzt
          {
            stepper.moveTo(shot[activeShot].position);      // aktivieren
            shot[activeShot].isFilled = true;               // Merken
            switchTime = millis();                          // Startzeit
            status = STATUS::fillpos;                       // Nächsten Schritt
          }
        }
        else                                                // Glas steht nicht drauf
        {
          digitalWrite(shot[activeShot].ledPin, ledOn);     // Visualisieren
          shot[activeShot].isFilled = false;                // Merker löschen
          if (++activeShot >= shotNums)                     // Nächstes Glas auswählen
          { activeShot = 0; }                               // Am Ende von vorn anfangen
        }
        checkFree();
      }
      break;
    case STATUS::prefill:
      for (byte b = 0; b < shotNums; b++)                   // Visualisierung
      { ledBlink(150); }
      // nur in der Ausgangslage prefill ausführbar!
      if (!digitalRead(prefillPin))                         // Button gedrückt
      { digitalWrite(relayPin, back2Home()); }              // in gesicherter Ausgangsposition Pumpe aktiv
      else                                                  // Button losgelassen
      {
        digitalWrite(relayPin, relayOff);                   // Pumpe aus
        allLed(ledOn);                                      // :-) Freigabevisualisierung
        status = STATUS::waitToStart;                       // gehe in Ausgangszustand
      }
      break;
    case STATUS::fillpos:
      ledBlink(70);                                         // Kleines blinkblink AuslaufLED
      if (millis() - switchTime > pauseTime)                // Zwangspause abgelaufen
      {
        digitalWrite(relayPin, relayOn);                    // Füllen starten
        switchTime = millis();                              // Startzeit merken
        status = STATUS::filling;                           // Nächster Schritt
      }
      break;
    case STATUS::filling:
      ledBlink(200);                                        // blinkblink
      if (millis() - switchTime > fillTime)                 // Füllzeit abgelaufen
      { status = STATUS::fillend; }                         // Nächster Schritt
      break;
    case STATUS::fillend:
      digitalWrite(shot[activeShot].ledPin, ledOff);        // gesichert AUS machen
      digitalWrite(relayPin, relayOff);                     // Füllen beenden
      switchTime = millis();                                // merken
      status = STATUS::homepos;                             // nächster Schritt
      break;
    case STATUS::homepos:
      if (millis() - switchTime > pauseTime)                // Zwangspause -> es tropft nach!
      {
        status = STATUS::waitToStart;                       // fange an von Vorn
      }
      break;
  }
  stepper.runToPosition();
}
//
void checkFree()
{
  bool toFill = false;                                      // Rückstellmerker vorbelegen
  for (byte b = 0; b < shotNums; b++)                       // Frage alle Stellplätze ab
  {
    if (!digitalRead(shot[b].switchPin) &&                  // es steht ein Glas drauf UND
        !shot[b].isFilled)                                  // es ist nicht gefüllt
    { toFill = true; }                                      // Füllanforderung merken
  }
  if (!toFill)                                              // Keine weitere Füllanforderung
  {
    back2Home();
    allLed(ledOn);
  }
}
//
bool back2Home()                                            //
{
  while (digitalRead(homePin))                              // Microtaster nicht ausgelöst
  {
    digitalWrite(dir_pin, links);                           // Stepper Richtung Endanschlag
    oneStep();                                              // Ein Stück fahren
    stepper.setCurrentPosition(0);                          // Null- / Startpunkt
  }
  stepper.moveTo(0);                                        // Feststellen
  return !digitalRead(homePin);                             // gibt true zurück, wenn ausgelöst
}
//
void oneStep()                                              // Macht wie es heisst
{
  digitalWrite(step_pin, HIGH);
  delay(5);
  digitalWrite(step_pin, LOW);
  delay(5);
}
//
void ledBlink(const byte blinkTime)         //
{
  if (millis() - shot[activeShot].ledTime > blinkTime)
  {
    digitalWrite(shot[activeShot].ledPin, !digitalRead(shot[activeShot].ledPin));
    shot[activeShot].ledTime = millis();
  }
}
//
void allLed(bool state)
{
  for (byte b = 0; b < shotNums; b++)
  {
    digitalWrite(shot[b].ledPin, state);
    shot[b].ledTime = millis();
  }
}

Das könnte ich dann nicht verantworten. :laughing:

Ich habe es ausgiebig getestet.
Und nu zum Ergebnis. Läuft leider nicht mehr so wirklich rund. :grimacing:

Das Homing zum Start und der Prefill funktioniert weiterhin.
Doch wenn ich jetzt ein Glas aufstelle, dann wird es zwar registriert und die jeweilige LED blinkt kurzzeitig sehr schnell, die Pumpe schaltet ein, aber der Stepper bleibt im Home stehen. Nach dem Füllvorgang muss das Glas weiterhin erst wieder heruntergenommen werden, bis es weitergeht. Der Stepper bleibt in allen Zuständen still und brav im Home stehen.