Go Down

Topic: Counter und Schrittmotor (Read 7658 times) previous topic - next topic

Draht

#540
Jan 13, 2021, 09:22 pm Last Edit: Jan 13, 2021, 09:22 pm by Draht
Ich denke da geht der Aufbau leichter von der Hand ;) . Hast Du auch gleich die passenden Buchsenleisten/Fassungen zum Stecken mitbestellt? Oder hast Du sowas da. Ich würde ihn schon steckbar machen.
Was würde dafür sprechen. Um ihn einfacher auszuwechseln sollte er hinüber sein?

MicroBahner

Um ihn einfacher auszuwechseln sollte er hinüber sein?
Genau. Solche Bauteile mache ich eigentlich immer steckbar, auch IC's. Aber das ist natürlich auch Ansichtssache.
Gruß, Franz-Peter

Draht

Genau. Solche Bauteile mache ich eigentlich immer steckbar, auch IC's. Aber das ist natürlich auch Ansichtssache.
Ja, den IC hätte ich auch steckbar gemacht. Beim Nano bin ich mir noch nicht so sicher. Es ist eben auch noch eine obtische Angelegenheit, da ja die ganze Elektronik sichtbar ist.

MicroBahner

da ja die ganze Elektronik sichtbar ist.
Das war mir jetzt nicht klar. Dann musst Du das ganz natürlich auch noch 'schön' machen  :smiley:
Gruß, Franz-Peter

Draht

Das war mir jetzt nicht klar. Dann musst Du das ganz natürlich auch noch 'schön' machen  :smiley:
Genau :) 

agmue

#545
Jan 14, 2021, 03:11 pm Last Edit: Jan 14, 2021, 03:12 pm by agmue
Da dieses Thema wegen der "Schönheit" gerade etwas verschnauft, komme ich nochmal mit der Idee, aus zwei Funktionen eine Methode zu machen.

Leider bereiten mir die Taster Kopfzerbrechen, denn diese Ausgabe sehe ich nicht:

Code: [Select]
    Taster.processButtons();
      if (Taster.clicked(stellTaster)) {
        debugln("Klick");
      }


Das im Aufbau befindliche Programm:

FarbUhr.ino:
Code: [Select]
//
//
// Debugging
#define DEBUG Serial // Diese Zeile als Kommentar, wenn keine Ausgabe auf dem seriellen Monitor gewünscht ist.
#ifdef DEBUG
#define debug(...) DEBUG.print(__VA_ARGS__)
#define debugln(...) DEBUG.println(__VA_ARGS__)
#define debugbegin(...) DEBUG.begin(__VA_ARGS__)
#define SECPERMIN 4  // Sekunden pro Minute - zur Beschleunignung beim Testen

#else
#define debug(...)
#define debugln(...)
#define debugbegin(...)
#endif


enum class Anzeige : byte {MIN, STD};

#include <MobaTools.h>
//#include "DS3231.h"
#include "ring.h"

void setup() {
  //Wire.begin();
  debugbegin(9600);
  debugln("\nStart");
  for (Ring &r : ringe) r.init();
  delay(50);
}

void loop() {
  for (Ring &r : ringe) r.run();
}


ring.h:
Code: [Select]
#define MAX8BUTTONS     // optional, spart Speicherplatz ( Standard ist maximal 16 Taster )

class Ring {
    enum : byte { refRing, refKell, stellTaster };
    const uint16_t FULLROT = 4080;

    const uint16_t NULLPOSMINUTEN = 171;  // Abstand Referenzpunkt ( LS ) zum Nullpunkt
    const uint16_t NULLPOSSTUNDEN = 100;
    const uint16_t NULLPOSSTDKELL = 10;
    const uint16_t NULLPOSMINKELL = 10;

    Anzeige anzeige;
    const byte RING_SPI, KELLE_SPI;
    const byte disPin, refRingPin, refKellPin, stellTasterPin;
    const byte inPins[3];

    MoToStepper StepRing;
    MoToStepper StepKelle;
    MoToButtons Taster;

  public:
    Ring( Anzeige anzeige, const byte RING_SPI, const byte KELLE_SPI, const byte disPin, const byte refRingPin, const byte refKellPin, const byte stellTasterPin )
      : anzeige(anzeige), RING_SPI(RING_SPI), KELLE_SPI(KELLE_SPI), disPin(disPin), refRingPin(refRingPin), refKellPin(refKellPin), stellTasterPin(stellTasterPin),
        inPins{refRingPin, refKellPin, stellTasterPin},
        StepRing(FULLROT), StepKelle(FULLROT), Taster(inPins, 2, 20, 2000)
    {}
    void init() {
      if (anzeige == Anzeige::MIN) {
        debug("Init Minuten\t");
      } else {
        debug("Init Stunden\t");
      }
      debug(disPin); debug('\t'); debug(refRingPin); debug('\t'); debug(refKellPin); debug('\t');
      debug(inPins[refRing]); debug('\t'); debug(inPins[refKell]); debug('\t'); debug(inPins[stellTaster]); debug('\n');
      pinMode(disPin, OUTPUT);     // Steuerung der Stepperspulen
      StepRing.attach( RING_SPI );
      StepRing.setRampLen(100);
      StepRing.setSpeed(50);
      StepKelle.attach( KELLE_SPI );
      StepKelle.setRampLen(100);
      StepKelle.setSpeed(50);
    }
    bool run() {
      Taster.processButtons();
      if (Taster.clicked(stellTaster)) {
        debugln("Klick");
      }
      return false;
    }
};

Ring ringe[] = {
  //anzeige, RING_SPI, KELLE_SPI, disPin, refRingPin, refKellPin, stellTasterPin
  {Anzeige::MIN, SPI_1, SPI_2, 8, 2, 4, A1}, // Minutenring
  {Anzeige::STD, SPI_3, SPI_4, 9, 3, 5, A2}  // Stundenring
};


Ausgabe auf einem Mega2560:

15:08:23.598 -> Start
15:08:23.598 -> Init Minuten 8 2 4 2 4 55
15:08:23.645 -> Init Stunden 9 3 5 3 5 56


Wo versteckt sich mein Fehler?

Die Vorstellungskraft ist wichtiger als Wissen, denn Wissen ist begrenzt. (Albert Einstein)

MicroBahner

#546
Jan 14, 2021, 05:19 pm Last Edit: Jan 14, 2021, 06:30 pm by MicroBahner
Wo versteckt sich mein Fehler?
Hier:
Code: [Select]
Taster(inPins, 2, 20, 2000)
Wenn man den MoToButtons sagt, dass nur 2 InputPins aktiv sind, werden auch nur 2 ausgewertet  ;) und stellTaster ist der 3. Pin:
Code: [Select]
enum : byte { refRing, refKell, stellTaster };
Es ist nicht der höchste Index im Array, sondern wirklich die Zahl der Elemente. Sizeof() hat da schon seine Vorteile ;)

Ich finde es aber sowieso keine gute Idee, für jeden Ring ein eigenes MoToButtons Objekt zu erzeugen. Zum einen ist es Ressourcenverschwendung ( ok, hier kein Problem ), zum anderen sind nicht alle notwendigen Taster  ringspezifisch. Es gibt ja auch noch den Taster, mit dem der Stellmodus aktiviert wird - und der ist ringübergreifend.


Edit: Diese Zeile in ring.h:
Code: [Select]
#define MAX8BUTTONS     // optional, spart Speicherplatz ( Standard ist maximal 16 Taster )
hat keine Auswirkung. Das define wirkt nur, wenn es vor dem Einbinden der MobaTools.h steht.
Gruß, Franz-Peter

agmue

Wenn man den MoToButtons sagt, dass nur 2 InputPins aktiv sind, werden auch nur 2 ausgewertet  ;)
Danke, jetzt macht es "Klick"!

Sizeof() hat da schon seine Vorteile ;)
Code: [Select]
Taster(inPins, anzahlTaster, 20, 2000)
Ja, da bin ich Deiner Meinung, aber wohin damit? Ich finde keine gute Stelle, bin daher auf dies verfallen:

Code: [Select]
enum : byte { refRing, refKell, stellTaster, anzahlTaster };
...
const byte inPins[anzahlTaster];
...
Taster(inPins, anzahlTaster, 20, 2000)

Wie geht das besser?

Ich finde es aber sowieso keine gute Idee, für jeden Ring ein eigenes MoToButtons Objekt zu erzeugen.
Ich fand das eine Super Idee, weil ich gerade das ausprobieren und lernen wollte ::)

Dies Zeile mit MAX8BUTTONS habe ich verschoben:

Code: [Select]
#define MAX8BUTTONS     // optional, spart Speicherplatz ( Standard ist maximal 16 Taster )
#include <MobaTools.h>


Die Vorstellungskraft ist wichtiger als Wissen, denn Wissen ist begrenzt. (Albert Einstein)

Draht

Bin nun auch wieder dabei  :)  und beobachte den weiteren Verlauf. Einbringen werde ich wahrscheinlich nicht, da es meine Progammierkentnisse übersteigt. Aber ich bin dabei.
Ich habe die Uhr jetzt mal ein paar Tage testlaufen lassen und habe gesehe, dass das mit der Referenzierung im Betrieb eine gute Idee war, denn es passieren da schon immer wieder Schrittfehler. Ich habe zwischendurch beobachtet, dass es auch schon mal etwa bis zu etwa 5 Grad sind!

MicroBahner

#549
Jan 16, 2021, 03:17 pm Last Edit: Jan 16, 2021, 03:54 pm by MicroBahner
...habe gesehe, dass das mit der Referenzierung im Betrieb eine gute Idee war, denn es passieren da schon immer wieder Schrittfehler. Ich habe zwischendurch beobachtet, dass es auch schon mal etwa bis zu etwa 5 Grad sind!
Ich denke, dass liegt daran, dass die Motore immer wieder ein- und ausgeschaltet werden. Denn dann haben sie in dern Pausen kein Haltemoment, und können beim Einschalten des Stroms auch schonmal im falschen Schritt 'einrasten'. Aber dafür ist ja die Referenzierung da.

So, und das wäre jetzt mal meine Klassenversion  ;)  .
Ziel war dabei, möglichst nahe am 'Original' zu bleiben. Basis ist die letzte Version, die ich in #521 gepostet hatte.

.ino:
Code: [Select]


// Debugging
#define DEBUG Serial // Diese Zeile als Kommentar, wenn keine Ausgabe auf dem seriellen Monitor gewünscht ist.
#ifdef DEBUG
#define debug(...) DEBUG.print(__VA_ARGS__)
#define debugln(...) DEBUG.println(__VA_ARGS__)
#define debugbegin(...) DEBUG.begin(__VA_ARGS__)
#define SECPERMIN 4  // Sekunden pro Minute - zur Beschleunignung beim Testen

#else
#define debug(...)
#define debugln(...)
#define debugbegin(...)
#endif

#include "Wire.h"
constexpr int DS3231_I2C_ADDRESS = 0x68; // Die Bibliothek möchte int!
#include <MobaTools.h>
MoToTimer Zeitlesen;

constexpr uint32_t PAUSE = 1000;
constexpr uint16_t FULLROT = 4080;

constexpr uint16_t NULLPOSMINUTEN = 171;  // Abstand Referenzpunkt ( LS ) zum Nullpunkt
constexpr uint16_t NULLPOSSTUNDEN = 100;
constexpr uint16_t NULLPOSSTDKELL = 10;
constexpr uint16_t NULLPOSMINKELL = 10;


/*MoToStepper StepMinuten(FULLROT);
MoToStepper StepMinKelle(FULLROT);
MoToStepper StepStunden(FULLROT2);
MoToStepper StepStdKelle(FULLROT);*/

const byte minDisPin = 8;      // disable Minuten-Schiebregister
const byte stdDisPin = 9;      // disable Stunden-Schiebregister

// Input-Pins ( Lichtschranken und Zeitsetz-Taster ) über MoToButtons verwalten
const byte inPins[] = { 2, 3, 4, 5, A1, A2, A3 };
const byte pinAnzahl = sizeof( inPins );
enum : byte { refMinPin, refStdPin, refMinKellPin, refStdKellPin, stellMin, stellStd, stellMode };
#define MAX8BUTTONS     // optional, spart Speicherplatz ( Standard ist maximal 16 Taster )
MoToButtons Taster ( inPins, pinAnzahl, 20, 2000 );  // Einrichten der Tasterverwaltung



//Variable für das Uhr stellen
boolean stellenAktiv;
const byte stellenLED = A0;

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ( (val / 16 * 10) + (val % 16) );
}

//Convertiert Dezimalzeichen in BCD Zeichen.
byte decToBcd(byte val){
  return ( (val/10*16) + (val%10) );
}

// Zeit von DS3231 holen
void readDS3231time( byte &minute, byte &hour)
{ // Die DS3231 wird nur jede 500ms abgefragt, sonst werden die
  // gespeicherten Zeiten zurückgegeben
  static byte DShour, DSmin, DSsec;
  if (!Zeitlesen.running()) {
    Zeitlesen.setTime( PAUSE );
    Wire.beginTransmission(DS3231_I2C_ADDRESS);
    Wire.write(0); // set DS3231 register pointer to 00h
    Wire.endTransmission();
    Wire.requestFrom(DS3231_I2C_ADDRESS, 3);
    // request three bytes of data from DS3231 starting from register 00h
    DSsec = bcdToDec(Wire.read() & 0x7f);
    DSmin = bcdToDec(Wire.read());
    DShour = bcdToDec(Wire.read() & 0x3f);

  }
   
#ifdef SECPERMIN
    if ( DSsec >= SECPERMIN && DSsec < 59 ) {
      DSsec = 59;
      Wire.beginTransmission(DS3231_I2C_ADDRESS);
      Wire.write(0);              // Start ab Register 0 (Sekunden )
      Wire.write(decToBcd(DSsec));    // Sekunden auf 59 setzen für vorzeitigen Minutensprung
      Wire.endTransmission();
    }
#endif
 
  hour = DShour;
  minute = DSmin;
}
// Anzeige im seriellen Monitor
void displayTime(byte second, byte minute, byte hour)
{
  char buf [10];
  snprintf ( buf, 10, "%02u:%02u:%02u", hour, minute, second );
  debugln(buf);  // send it to the serial monitor
}

#include "ringClass.h"
Ring minRing;   // 1. Instanz sind Minuten
Ring stdRing;   // 2. Instanz sind die Stunden

void setup() {
  Wire.begin();
  debugbegin(115200);
  debugln("\nStart");
  // pinMode für die Tater/LS-Eingänge wird in MoToButtons gemacht
  //pinMode(minDisPin, OUTPUT);     // Steuerung der Stepperspulen 1 und 2
  //pinMode(stdDisPin, OUTPUT);     // Steuerung der Stepperspulen 3 und 4
  pinMode(stellenLED, OUTPUT);

  /*StepMinuten.attach( SPI_1 );
  StepMinuten.setRampLen(100);
  StepMinuten.setSpeed(50);
  StepMinKelle.attach( SPI_2);
  StepMinKelle.setRampLen(100);
  StepMinKelle.setSpeed(50);*/
  minRing.init( SPI_1, 100, 50,  NULLPOSMINUTEN, SPI_2, 100, 50, NULLPOSMINKELL, minDisPin, refMinPin, refMinKellPin, stellMin );

  /*StepStunden.attach( SPI_3 );
  StepStunden.setRampLen(100);
  StepStunden.setSpeed(50);
  StepStdKelle.attach( SPI_4 );
  StepStdKelle.setRampLen(100);
  StepStdKelle.setSpeed(50);*/
  stdRing.init( SPI_3, 100, 50, NULLPOSSTUNDEN, SPI_4, 100, 50, NULLPOSSTDKELL, stdDisPin, refStdPin, refStdKellPin, stellStd );
  delay(100);
}

void loop () {
  Taster.processButtons();
  int minutenPosition = minRing.process(stellenAktiv);
  int stundenPosition = stdRing.process(stellenAktiv);

  // Verwaltung Einstellmode
  if (! stellenAktiv ) {
    // Wenn Taster lange gedrückt, in Stellmode schalten
    if ( Taster.longPress(stellMode) ) {
      stellenAktiv = true;
      debugln("Stellmode aktiv");
      digitalWrite( stellenLED, HIGH );
    }
  } else {
     // Das Beenden des Stellmode ist nur im Stillstand der Ringe möglich
    if ( Taster.shortPress(stellMode) ) {
      debug (F("minutenPosition=")); debugln(minutenPosition);
      debug(F("stundenPosition=")); debugln(stundenPosition);
      if ( minutenPosition >= 0 && stundenPosition >= 0 ) {
        debugln(F("Stellmode beenden"));
        debug("Std: "); debug(stundenPosition); debug(" - Min: "); debugln(minutenPosition * 5);

        // aktuelle Stunden und Minuten an RTC übertragen
        Wire.beginTransmission(DS3231_I2C_ADDRESS);
        Wire.write(0);              // Start ab Register 0 (Sekunden )
        Wire.write(decToBcd(0));    // Sekunden
        Wire.write(decToBcd(minutenPosition * 5)); // Minuten
        Wire.write(decToBcd(stundenPosition));    // Stunde
        Wire.endTransmission();
        digitalWrite( stellenLED, LOW );
        stellenAktiv = false;
      }
    }

}
}
Gruß, Franz-Peter

MicroBahner

#550
Jan 16, 2021, 03:19 pm Last Edit: Jan 16, 2021, 05:54 pm by MicroBahner
Vielleicht erkennt Draht ja das eine oder andere wieder, und kann das Programm nachvollziehen.
Hier noch die Klassendefinition 'ringClass.h', die in der ino eingebunden wird:
Code: [Select]


class Ring {
    enum Schritt : byte {REF_CHK_KELLE, REF_FREI_KELLE, REF_KELLE, REF_GEFUNDEN, REF_M, REF_SUCHEN, WARTEN, KELLE_RUNTER, M_ZEIT, KELLE_RAUF};
    enum  : byte { MIN, STD }; // 1. Instanz: Minuten, 2. Instanz Stunden
    static byte ringIx;
    static char *ringTxt[2] ;
  private:
    MoToStepper StepRing = { FULLROT };
    MoToStepper StepKelle = { FULLROT };

    byte _disPin;   // Output um Stepper abzuschalten
    byte _refLsRing;
    byte _refLsKelle;
    byte _stellTaster;
    int  _nullposKelle;
    int  _nullposRing;
    byte _ringNr;
    byte minute, hour;
    byte ringPosAlt = 12, ringPosNeu = 0;
    Ring::Schritt schritt = Schritt::REF_CHK_KELLE, altSchritt = Schritt::REF_M;

    void printSchritt( byte schritt ) {
      static const char *schrittTexte[] = {"REF_CHK_KELLE", "REF_FREI_KELLE", "REF_KELLE", "REF_GEFUNDEN", "REF_M", "REF_SUCHEN", "WARTEN", "KELLE_RUNTER", "M_ZEIT", "KELLE_RAUF" };
      debug(ringTxt[_ringNr]); debugln( schrittTexte[schritt] );
    }


  public:
    Ring () {
       _ringNr = ringIx++;       // interner Index: 0: MinutenRIng ( erste Instanz ), 1 Stundenring ( 2.Instanz )
    }
    void init( byte stepperRing, int rampRing, int speedRing, int nullposRing, byte stepperKelle, int rampKelle, int speedKelle, int nullposKelle, byte disPin, byte refLsRing, byte refLsKelle, byte stellTaster ) {
      StepRing.attach(stepperRing);
      StepRing.setRampLen(rampRing);
      StepRing.setSpeed(speedRing);
      StepKelle.attach(stepperKelle);
      StepKelle.setRampLen(rampKelle);
      StepKelle.setSpeed(speedKelle);
      _disPin = disPin;
      pinMode( _disPin, OUTPUT );
      _refLsRing = refLsRing;
      _refLsKelle = refLsKelle;
      _stellTaster = stellTaster;
      _nullposKelle = nullposKelle;
      _nullposRing = nullposRing;
    }


    // Rückgabe wert ist die aktuelle Position ( wenn im Zustand WARTEN )
    // oder -1 wenn in Bewegung
    int process(boolean _stellenAktiv) {
#ifdef DEBUG
      if ( schritt != altSchritt ) {
        // Debugging; Ausgabe wenn Statuswechsel
        printSchritt((byte) schritt);
        altSchritt = schritt;
      }
#endif

      // Erkennen der Referenzpunkte ( unabhängig von der FSM )
      // Kellen-Referenz
      if (Taster.pressed(_refLsKelle)) {   // Wenn Referenzpunkt erreicht, Bewegung anhalten und Nullpunkt setzen.
        StepKelle.setZero(_nullposKelle);  // Nullpunkt etwas hinter den referenzpunkt setzen, um ihn auch bei Schrittverlusten sicher zu erreichen
        StepKelle.write(0);
        debug(ringTxt[_ringNr]); debugln("RefKelle erreicht");
      }
      // Ring-Referenz
      if (Taster.pressed(_refLsRing)) {
        StepRing.setZero(_nullposRing);
        StepRing.write(0);
        debug(ringTxt[_ringNr]); debugln("Ref-Ring erreicht");
      }

      switch (schritt) {
        case Schritt:: REF_CHK_KELLE:
          // Prüfen ob Kelle in der Lichtschranke steht
          digitalWrite(_disPin, LOW);                 // Stepperspulen anschalten
          if ( Taster.state(_refLsKelle ) ) {
            // Kelle aus LS ausfahren
            debug(ringTxt[_ringNr]); debugln(F("LS freifahren"));
            StepKelle.write( -159 );
            schritt = Schritt::REF_FREI_KELLE;
          } else {
            // Kelle Refpunkt suchen
            schritt = Schritt::REF_KELLE;
          }
          break;

        case Schritt::REF_FREI_KELLE:
          // LS freifahren
          // etwas über LS Grenze hinausfahren
          if ( Taster.released(_refLsKelle) ) StepKelle.doSteps( -100 );
          if ( !StepKelle.moving() ) schritt = Schritt::REF_KELLE;
          break;

        case Schritt::REF_KELLE:
          StepKelle.write(180);
          schritt = Schritt::REF_GEFUNDEN;
          debug(ringTxt[_ringNr]); debugln(F("Referenzpunkt Kelle suchen"));  // Bewegung Kelle starten.
          break;

        case Schritt::REF_GEFUNDEN:
          if (Taster.state(_refLsKelle)) {   // Wenn Referenzpunkt erreicht, Bewegung anhalten und Nullpunkt setzen.
            debug(ringTxt[_ringNr]); debugln(F("Referenzpunkt Kelle gefunden"));
            schritt = Schritt::REF_M;
          }
          break;

        case Schritt::REF_M:

          debug(ringTxt[_ringNr]); debugln(F("Referenzpunkt Ring suche"));
          StepRing.write(390);
          schritt = Schritt::REF_SUCHEN;
          break;

        case Schritt::REF_SUCHEN:

          if (Taster.state(_refLsRing) ) {
            debug(ringTxt[_ringNr]); debugln(F("Referenzpunkt Ring gefunden"));
            schritt = Schritt::WARTEN;

          }
          break;
        case Schritt::WARTEN:

          // Das Abschalten der Stepper wurde bereits in KELLE_RAUF gemacht
          if ( _stellenAktiv ) {
            // im Einstellmodus wird das vorrücken über den Stelltaster ausgelöst
            ringPosNeu = ringPosAlt + Taster.clicked(_stellTaster); // Bei einfach Klick wird eins, bei Doppelklick 2 addiert
            if ( ringPosNeu > 11 ) ringPosNeu = 0;
          } else {
            // im Normalbetrieb die Uhr abfragen
            readDS3231time(minute, hour);
            if ( _ringNr == MIN ) {
              ringPosNeu = minute / 5;
            } else {
              ringPosNeu = hour > 11 ? hour - 12 : hour;     //posStunden = minute > 11 ? minute - 12 : minute; // nur zum Testen
            }
          }
          if (ringPosAlt != ringPosNeu) {
            // Ereignis: Zeitpunkt für den Bewegungsablauf ist gekommen
            // Aktion: Ablauf starten mit der Kellenbewegung
            debug(ringTxt[_ringNr]);; displayTime(0, minute, hour);

            debug(ringTxt[_ringNr]); debug(F("alt: ")); debug(ringPosAlt); debug('\t'); debug("pos: "); debug(ringPosNeu); debug('\n');
            ringPosAlt = ringPosNeu;
            digitalWrite(_disPin, LOW);                 // Stepperspulen anschalten
            StepKelle.write(0);                // Minutenkelle runter
            schritt = Schritt::KELLE_RUNTER;
          }
          break;

        case Schritt::KELLE_RUNTER:

          if (!StepKelle.moving()) {
            // Ereignis: die Minutenkelle ist unten
            // Aktion: Beweegung des Minutenrings starten
            if (ringPosNeu == 0) {
              StepRing.write(360);
              schritt = Schritt::M_ZEIT;
            }
            else {
              StepRing.write(30 * ringPosNeu);
              debug(ringTxt[_ringNr]); debug("Winkel: "); debugln(30 * ringPosNeu);

              schritt = Schritt::M_ZEIT;
            }
          }

          break;
        case Schritt::M_ZEIT:

          if (!StepRing.moving() )
          { // Ereignis: Bewegung des Rings beendet
            // Aktion: Kelle hochfahren

            StepKelle.write(-59);           // Minutenkelle hoch

            // if (ringPosNeu == 0) StepRing.setZero();

            schritt = Schritt::KELLE_RAUF;
          }
          break;

        case Schritt::KELLE_RAUF:
          if (!StepKelle.moving())
          { // Ereignis: die Minutenkelle ist oben
            // Aktion: Stepper abschalten
            digitalWrite(_disPin, HIGH);
            schritt = Schritt::WARTEN;
          }
          break;
      }

      if ( schritt == Schritt::WARTEN ) return ringPosAlt;
      else return -1;
    }


};

// Klassenglobale Variable
byte Ring::ringIx = 0;
char *Ring::ringTxt[2] = {"Min: ", "Std: "};



Edit: Ich hab' noch gesehen, dass da an einigen Stellen ( im Kommentar ) noch 'Minuten' steht. Das stimmt natürlich jetzt nicht mehr. Die Klasse gilt ja für beide Ringe. Die Variablennamen hatte ich schon auf 'neutral' geändert, bei den Kommentaren hab' ichs vergessen :(
Gruß, Franz-Peter

agmue

Leider mag mich die Forensoftware momentan nicht besonders, ich kann nur sporadisch schreiben :(


@MicroBahner: Applaus, Applaus!

Pingelige Anmerkungen:

1) warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
 char *Ring::ringTxt[2] = {"Min: ", "Std: "};
                                           ^


2) Hat "private" in class Ring eine besondere Bedeutung, weil class anders als struct doch sowieso mit private anfängt?

Die Vorstellungskraft ist wichtiger als Wissen, denn Wissen ist begrenzt. (Albert Einstein)

michael_x

bei
static const char *schrittTexte[] ...

hat MicroBahner es schon richtig gemacht.

Eigentlich ist die Unterscheidung zwischen static und echten privaten Variablen einer Instanz doch hübsch, finde ich.

Dem "Applaus!" möchte ich mich anschliessen, obwohl eine enum als Index 0 / 1 zu verwenden zurecht nicht in jeder Sprache erlaubt ist. :)

Draht

Vielleicht erkennt Draht ja das eine oder andere wieder, und kann das Programm nachvollziehen.
Hier noch die Klassendefinition 'ringClass.h', die in der ino eingebunden wird:
Code: [Select]


class Ring {
    enum Schritt : byte {REF_CHK_KELLE, REF_FREI_KELLE, REF_KELLE, REF_GEFUNDEN, REF_M, REF_SUCHEN, WARTEN, KELLE_RUNTER, M_ZEIT, KELLE_RAUF};
    enum  : byte { MIN, STD }; // 1. Instanz: Minuten, 2. Instanz Stunden
    static byte ringIx;
    static char *ringTxt[2] ;
  private:
    MoToStepper StepRing = { FULLROT };
    MoToStepper StepKelle = { FULLROT };

    byte _disPin;   // Output um Stepper abzuschalten
    byte _refLsRing;
    byte _refLsKelle;
    byte _stellTaster;
    int  _nullposKelle;
    int  _nullposRing;
    byte _ringNr;
    byte minute, hour;
    byte ringPosAlt = 12, ringPosNeu = 0;
    Ring::Schritt schritt = Schritt::REF_CHK_KELLE, altSchritt = Schritt::REF_M;

    void printSchritt( byte schritt ) {
      static const char *schrittTexte[] = {"REF_CHK_KELLE", "REF_FREI_KELLE", "REF_KELLE", "REF_GEFUNDEN", "REF_M", "REF_SUCHEN", "WARTEN", "KELLE_RUNTER", "M_ZEIT", "KELLE_RAUF" };
      debug(ringTxt[_ringNr]); debugln( schrittTexte[schritt] );
    }


  public:
    Ring () {
       _ringNr = ringIx++;       // interner Index: 0: MinutenRIng ( erste Instanz ), 1 Stundenring ( 2.Instanz )
    }
    void init( byte stepperRing, int rampRing, int speedRing, int nullposRing, byte stepperKelle, int rampKelle, int speedKelle, int nullposKelle, byte disPin, byte refLsRing, byte refLsKelle, byte stellTaster ) {
      StepRing.attach(stepperRing);
      StepRing.setRampLen(rampRing);
      StepRing.setSpeed(speedRing);
      StepKelle.attach(stepperKelle);
      StepKelle.setRampLen(rampKelle);
      StepKelle.setSpeed(speedKelle);
      _disPin = disPin;
      pinMode( _disPin, OUTPUT );
      _refLsRing = refLsRing;
      _refLsKelle = refLsKelle;
      _stellTaster = stellTaster;
      _nullposKelle = nullposKelle;
      _nullposRing = nullposRing;
    }


    // Rückgabe wert ist die aktuelle Position ( wenn im Zustand WARTEN )
    // oder -1 wenn in Bewegung
    int process(boolean _stellenAktiv) {
#ifdef DEBUG
      if ( schritt != altSchritt ) {
        // Debugging; Ausgabe wenn Statuswechsel
        printSchritt((byte) schritt);
        altSchritt = schritt;
      }
#endif

      // Erkennen der Referenzpunkte ( unabhängig von der FSM )
      // Kellen-Referenz
      if (Taster.pressed(_refLsKelle)) {   // Wenn Referenzpunkt erreicht, Bewegung anhalten und Nullpunkt setzen.
        StepKelle.setZero(_nullposKelle);  // Nullpunkt etwas hinter den referenzpunkt setzen, um ihn auch bei Schrittverlusten sicher zu erreichen
        StepKelle.write(0);
        debug(ringTxt[_ringNr]); debugln("RefKelle erreicht");
      }
      // Ring-Referenz
      if (Taster.pressed(_refLsRing)) {
        StepRing.setZero(_nullposRing);
        StepRing.write(0);
        debug(ringTxt[_ringNr]); debugln("Ref-Ring erreicht");
      }

      switch (schritt) {
        case Schritt:: REF_CHK_KELLE:
          // Prüfen ob Kelle in der Lichtschranke steht
          digitalWrite(_disPin, LOW);                 // Stepperspulen anschalten
          if ( Taster.state(_refLsKelle ) ) {
            // Kelle aus LS ausfahren
            debug(ringTxt[_ringNr]); debugln(F("LS freifahren"));
            StepKelle.write( -159 );
            schritt = Schritt::REF_FREI_KELLE;
          } else {
            // Kelle Refpunkt suchen
            schritt = Schritt::REF_KELLE;
          }
          break;

        case Schritt::REF_FREI_KELLE:
          // LS freifahren
          // etwas über LS Grenze hinausfahren
          if ( Taster.released(_refLsKelle) ) StepKelle.doSteps( -100 );
          if ( !StepKelle.moving() ) schritt = Schritt::REF_KELLE;
          break;

        case Schritt::REF_KELLE:
          StepKelle.write(180);
          schritt = Schritt::REF_GEFUNDEN;
          debug(ringTxt[_ringNr]); debugln(F("Referenzpunkt Kelle suchen"));  // Bewegung Kelle starten.
          break;

        case Schritt::REF_GEFUNDEN:
          if (Taster.state(_refLsKelle)) {   // Wenn Referenzpunkt erreicht, Bewegung anhalten und Nullpunkt setzen.
            debug(ringTxt[_ringNr]); debugln(F("Referenzpunkt Kelle gefunden"));
            schritt = Schritt::REF_M;
          }
          break;

        case Schritt::REF_M:

          debug(ringTxt[_ringNr]); debugln(F("Referenzpunkt Ring suche"));
          StepRing.write(390);
          schritt = Schritt::REF_SUCHEN;
          break;

        case Schritt::REF_SUCHEN:

          if (Taster.state(_refLsRing) ) {
            debug(ringTxt[_ringNr]); debugln(F("Referenzpunkt Ring gefunden"));
            schritt = Schritt::WARTEN;

          }
          break;
        case Schritt::WARTEN:

          // Das Abschalten der Stepper wurde bereits in KELLE_RAUF gemacht
          if ( _stellenAktiv ) {
            // im Einstellmodus wird das vorrücken über den Stelltaster ausgelöst
            ringPosNeu = ringPosAlt + Taster.clicked(_stellTaster); // Bei einfach Klick wird eins, bei Doppelklick 2 addiert
            if ( ringPosNeu > 11 ) ringPosNeu = 0;
          } else {
            // im Normalbetrieb die Uhr abfragen
            readDS3231time(minute, hour);
            if ( _ringNr == MIN ) {
              ringPosNeu = minute / 5;
            } else {
              ringPosNeu = hour > 11 ? hour - 12 : hour;     //posStunden = minute > 11 ? minute - 12 : minute; // nur zum Testen
            }
          }
          if (ringPosAlt != ringPosNeu) {
            // Ereignis: Zeitpunkt für den Bewegungsablauf ist gekommen
            // Aktion: Ablauf starten mit der Kellenbewegung
            debug(ringTxt[_ringNr]);; displayTime(0, minute, hour);

            debug(ringTxt[_ringNr]); debug(F("alt: ")); debug(ringPosAlt); debug('\t'); debug("pos: "); debug(ringPosNeu); debug('\n');
            ringPosAlt = ringPosNeu;
            digitalWrite(_disPin, LOW);                 // Stepperspulen anschalten
            StepKelle.write(0);                // Minutenkelle runter
            schritt = Schritt::KELLE_RUNTER;
          }
          break;

        case Schritt::KELLE_RUNTER:

          if (!StepKelle.moving()) {
            // Ereignis: die Minutenkelle ist unten
            // Aktion: Beweegung des Minutenrings starten
            if (ringPosNeu == 0) {
              StepRing.write(360);
              schritt = Schritt::M_ZEIT;
            }
            else {
              StepRing.write(30 * ringPosNeu);
              debug(ringTxt[_ringNr]); debug("Winkel: "); debugln(30 * ringPosNeu);

              schritt = Schritt::M_ZEIT;
            }
          }

          break;
        case Schritt::M_ZEIT:

          if (!StepRing.moving() )
          { // Ereignis: Bewegung des Rings beendet
            // Aktion: Kelle hochfahren

            StepKelle.write(-59);           // Minutenkelle hoch

            // if (ringPosNeu == 0) StepRing.setZero();

            schritt = Schritt::KELLE_RAUF;
          }
          break;

        case Schritt::KELLE_RAUF:
          if (!StepKelle.moving())
          { // Ereignis: die Minutenkelle ist oben
            // Aktion: Stepper abschalten
            digitalWrite(_disPin, HIGH);
            schritt = Schritt::WARTEN;
          }
          break;
      }

      if ( schritt == Schritt::WARTEN ) return ringPosAlt;
      else return -1;
    }


};

// Klassenglobale Variable
byte Ring::ringIx = 0;
char *Ring::ringTxt[2] = {"Min: ", "Std: "};



Edit: Ich hab' noch gesehen, dass da an einigen Stellen ( im Kommentar ) noch 'Minuten' steht. Das stimmt natürlich jetzt nicht mehr. Die Klasse gilt ja für beide Ringe. Die Variablennamen hatte ich schon auf 'neutral' geändert, bei den Kommentaren hab' ichs vergessen :(
Sehr Schick! Vielen Dank! Werde es auch mal testen. Wo muss ich das:  ringClass.h abspeichern?

MicroBahner

Wo muss ich das:  ringClass.h abspeichern?
Im ino-Verzeichnis, genauso wie vorher die Files auch. Nur dass der Name - und speziell auch die Namenserweiterung ( .h )  anders ist. Dadurch bindet die IDE das nicht mehr automatisch zusammen, sondern es wird mit #include an der richtigen Stelle eingefügt.
Gruß, Franz-Peter

Go Up