Counter und Schrittmotor

So nun sind die beiden DS3231 bei mir angekommen. Mein alter Taktgeber ist inzwischen auch wieder auferstanden! Mit dem läuft das Programm eigentlich auch prima. Da ich aber nun die DS3231 habe und mir diese von euch auch empfohlen wurde, werde ich mich mal mit diesen resp. mit dem Sketch befassen.

Draht:
Da ich aber nun die DS3231 habe und mir diese von euch auch empfohlen wurde, werde ich mich mal mit diesen resp. mit dem Sketch befassen.

Da hätte ich dann einen Vorschlag als möglichen Ausgangspunkt für eigene Überlegungen:

// 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__)
#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 = 4096;
constexpr uint16_t NULLPOSMINUTEN = 2850;
constexpr uint16_t NULLPOSSTUNDEN = 3100;
MoToStepper StepMinuten(FULLROT);
MoToStepper StepMinKelle(FULLROT);
MoToStepper StepStunden(FULLROT);
constexpr byte refMinPin = 3;
constexpr byte refStdPin = 4;

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ( (val / 16 * 10) + (val % 16) );
}
// Zeit von DS3231 holen
void readDS3231time(byte *second, byte *minute, byte *hour)
{
  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
  *second = bcdToDec(Wire.read() & 0x7f);
  *minute = bcdToDec(Wire.read());
  *hour = bcdToDec(Wire.read() & 0x3f);
}
// 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
}

void setup() {
  Wire.begin();
  debugbegin(115200);
  debugln("\nStart");
  pinMode( refMinPin, INPUT_PULLUP );
  pinMode( refStdPin, INPUT_PULLUP );
  StepMinuten.attach( 8, 7, 6, 5 );
  StepMinuten.setSpeed(50);

  StepMinKelle.attach( 9, 10, 11, 12 );
  StepMinKelle.setSpeed(50);

  StepStunden.attach( A0, A1, A2, A3 );
  StepStunden.setSpeed(50);
}

void loop() {
  byte second, minute, hour;
  static byte altMinuten = 0, posMinuten = 0, altStunden = 0, posStunden = 0;
  enum class Schritt : byte {REF_START, REF_SUCHEN, WARTEN, KELLE_RUNTER, ZEIT, KELLE_RAUF};
  static Schritt schritt = Schritt::REF_START;
  switch (schritt) {
    case Schritt::REF_START:
      debugln("Referenztastersuche Minuten und Stunden");
      StepMinuten.write(400);
      StepStunden.write(400);
      schritt = Schritt::REF_SUCHEN;
      break;
    case Schritt::REF_SUCHEN:
      if (!digitalRead(refMinPin)) {
        StepMinuten.setZero(NULLPOSMINUTEN);
        StepMinuten.write(0);
      }
      if (!digitalRead(refStdPin)) {
        StepStunden.setZero(NULLPOSSTUNDEN);
        StepStunden.write(0);
      }
      if (!StepMinuten.moving() && !StepStunden.moving()) {
        debugln("Referenztaster Minuten und Stunden gefunden");
        schritt = Schritt::WARTEN;
      }
      break;
    case Schritt::WARTEN:
      if (!Zeitlesen.running()) {
        Zeitlesen.setTime( PAUSE );
        readDS3231time(&second, &minute, &hour);
        displayTime(second, minute, hour);
        posMinuten = minute / 5;
        posStunden = hour > 11 ? hour - 12 : hour;
        //posMinuten = second / 5;                         // nur zum Testen
        //posStunden = minute > 11 ? minute - 12 : minute; // nur zum Testen
        if (altMinuten != posMinuten) {
          debug("altMinuten: "); debug(altMinuten); debug('\t'); debug("posMinuten: "); debug(posMinuten); debug('\n');
          altMinuten = posMinuten;
          debugln("Kelle runter");
          schritt = Schritt::KELLE_RUNTER;
        }
      }
      break;
    case Schritt::KELLE_RUNTER:
      StepMinKelle.doSteps(512);
      schritt = Schritt::ZEIT;
      break;
    case Schritt::ZEIT:
      if (!StepMinKelle.moving()) {
        if (posMinuten == 0) {
          StepMinuten.write(360);
          debug("Minuten-Winkel: "); debug(360); debug('\t');
        } else {
          StepMinuten.write(30 * posMinuten);
          debug("Minuten-Winkel: "); debug(30 * posMinuten); debug('\t');
        }
        if (altStunden != posStunden) {
          altStunden = posStunden;
          if (posStunden == 0) {
            StepStunden.write(360);
            debug("Stunden-Winkel: "); debug(360);
          } else {
            StepStunden.write(30 * posStunden);
            debug("Stunden-Winkel: "); debug(30 * posStunden);
          }
        }
        debug('\n');
        debugln("Kelle rauf");
        schritt = Schritt::KELLE_RAUF;
      }
      break;
    case Schritt::KELLE_RAUF:
      if (!StepMinuten.moving() && !StepStunden.moving()) {
        if (posMinuten == 0) StepMinuten.setZero();
        if (posStunden == 0) StepStunden.setZero();
        StepMinKelle.doSteps(-512);
        schritt = Schritt::WARTEN;
      }
      break;
  }
}

TODO: Referenzpunkte Kellen, Zeiteinstellung

Weil ich was lernen will, habe ich von combie constexpr und enum class probiert. Bitte lasse Dich davon nicht irritieren :smiley:

@agmue: Wow das sieht schön aus. Nicht das ich das alles schon verstehe, aber das scheint so zu funktionieren. Die Stunden brauchen auch noch 'Kellen runter und hoch'. Wird etwas 'knapp' mit den freien Pinplätzen. Sonst muss ich das Ganze auf 2 Atmega aufteilen. Einer für die Minuten + Kellen (rauf/runter) und einer für die Stunden + Kellen (rauf/runter).
Was meinst du mit TODO: Referenzpunkte Kellen?

Draht:
Sonst muss ich das Ganze auf 2 Atmega aufteilen.

Das wird vielleicht nicht nötig sein.
Mehr Ports wären ja auch mit 74HC595 oder MCP23017 möglich.

Gruß Walter

Draht:
Wird etwas 'knapp' mit den freien Pinplätzen. Sonst muss ich das Ganze auf 2 Atmega aufteilen.

4 von diesen unipolaren Steppern können die MobaTools per SPI direkt über einfache Schieberegister ansteuern. Auf der letzten Seite der Doku ist dazu ein Schaltbild mit HC4094 dabei. Es geht aber auch mit den von Walter angesprochenen 74HC595.

Draht:
Was meinst du mit TODO: Referenzpunkte Kellen?

Nach Reset ist die Stellung eines Schrittmotors unbekannt, weshalb zunächst mit dem Referenzpunkt eine absolute Position ermittelt werden muß. Das gilt für alle vier Schrittmotoren, also auch für die Kellen. Oder?

Draht:
Sonst muss ich das Ganze auf 2 Atmega aufteilen.

Der Mega2560 wäre für Dich genau richtig, weil es mit zwei UNOs oder Nanos komplizierter wird. Als Mega2560 Pro Mini ist er kleiner.

Oder eben MobaTools per SPI mit Schieberegister.

Würden die MobaTools auf dem ESP32 laufen, hätte ich den noch mit ins Spiel gebracht, weil Du dann das Stellen der Uhr über Zeitserver im Internet elegant ohne zusätzliche Pins für Taster lösen könntest. Aber das geht leider nicht :cry:

agmue:
Das gilt für alle vier Schrittmotoren, also auch für die Kellen. Oder?

Ja, voralle für die Kellen rauf/runter, wäre natürlich aber eigentlich auch gut für die Minuten/Stunden-Drehung.

agmue:
Nach Reset ist die Stellung eines Schrittmotors unbekannt, weshalb zunächst mit dem Referenzpunkt eine absolute Position ermittelt werden muß.

Ok, ich schau mal ob ich da Informtion darüber finde.

Jetzt mal bevor ich mich weiter mit dem Studium euerer neuen Vorschläge befasse mal eine ander Überlegung - eine art Notausgang für den Fall der Fälle. Da ich mich langsam aber sicher auf Terrain begebe wo ich nicht mehr alles verstehe und wahrscheinlich noch SEHR viel Zeit brauch um soweit zu sein.

Also, wäre es nicht auch in etwa so gegangen:
Der Timer DS 3231 steuert einen Atmega (oä) auf dem der Counter Sketch läuft und zwar der mit <Stepper.h> ganz am Anfang dieses Threads. dieser Counter steuert mit Ausgang A einen weiteren Atmega für die Minuten und Stunden und mit Ausgang B die Kellen rauf/runter Funktion. Nur so grob.
So und nun wieder zurück. :confused:

Für die Erkennung des Referenzpunkts verwende ich beispielsweise Reflektierender Objektsensor OPTEK OPB743, weil berührungslos und gut passend zum Arduino PullUp-Widerstand. Etwas Alufolie oder dergleichen genügt für die Reflexion.

DS3231 hat einen 32 kHz Taktausgang als Rückfallszenario. Aber nach meiner Einschätzung macht es das nicht einfacher, weil Referenzpunkt und Uhreinstellen brauchst Du auch.

Ist das eigentlich eine Bastelei für Dich oder willst Du das an ahnungslose Kunden verkaufen? Diese Frage zielt auf die Bedienung.

agmue:
Für die Erkennung des Referenzpunkts verwende ich beispielsweise Reflektierender Objektsensor OPTEK OPB743, weil berührungslos und gut passend zum Arduino PullUp-Widerstand. Etwas Alufolie oder dergleichen genügt für die Reflexion.

oder ein Reed-Kontakt? Ist wahrscheinlich zu ungenau oder?

Draht:
Ist das eigentlich eine Bastelei für Dich oder willst Du das an ahnungslose Kunden verkaufen? Diese Frage zielt auf die Bedienung.

Also im Moment mach ich die Uhr erstmal für mich, mit der Option für 'ahnungslose Kunden' D.h. wenn die Bedienung nicht allzu kompliziert wäre, wäre es nicht schlecht. :wink:

Draht:
oder ein Reed-Kontakt? Ist wahrscheinlich zu ungenau oder?

Für Deine Anwendung genau genug, allerdings prellt der.

Draht:
D.h. wenn die Bedienung nicht allzu kompliziert wäre, wäre es nicht schlecht. :wink:

Da kommt dann der ESP32 ins Spiel, der sich die exakte Uhrzeit einschließlich Sommer/Winter-Umschaltung per WLAN aus dem Internet holen könnte. Das wäre von der Bedienung einfach, nur zum Programmieren für Fortgeschrittene. Aber der ESP32 kann in der ersten Entwicklungsstufe ganz ohne WLAN und Verbindung ins Internet die Schrittmotore bewegen.

Ich möchte Dich aber zu nichts überreden, sondern Dir nur technisch machbare Alternativen aufzeigen.

agmue:
Für Deine Anwendung genau genug, allerdings prellt der.

Dann evtl. den da: Broadcom Objekt-Sensor HSDL-9100-021 HSDL-9100-021 1 St. – Conrad Electronic Schweiz

Weil deiner hat mein Anbieter nicht im Sortiment

agmue:
Da kommt dann der ESP32 ins Spiel, der sich die exakte Uhrzeit einschließlich Sommer/Winter-Umschaltung per WLAN aus dem Internet holen könnte. Das wäre von der Bedienung einfach, nur zum Programmieren für Fortgeschrittene.

Eben für Fortgeschritte, also nix für mich!! Ich bin schon mit der aktuellen Materie leicht überfordert. :confused:

Da meine Uhr vom 'Uhrableser' sowieso schon etwas 'Gehirnbewegung' abverlangt und nicht für Ottonormalverbraucher ist, muss die Bedienung jetzt auch nicht SOOO für ganz unpraktische sein. Also um die Uhrzeit ab und zu zu justieren oä. das sollten man denen schon zutrauen müssen.

Schaue Dir mal die Anleitung: Einführung zu fipsok.de, in welchem Maß Dich das abschreckt.

Sonst eben UNO/Nano mit Schieberegister oder Mega2560.

agmue:
Schaue Dir mal die Anleitung: Einführung zu fipsok.de, in welchem Maß Dich das abschreckt.

Sonst eben UNO/Nano mit Schieberegister oder Mega2560.

Sehr gute Anleitung/Einführung, aber ich bleib erstmal bei UNO und Schieberegister da komm ich im Moment am ehsten über die Runden. Das Projekt soll ja dann auch mal gelegentlich zu einem vorläufigen Ende kommen :wink:

Also meine ToDo list für den Moment ist:

  1. Eruiren wie das mit dem Schieberegister zu machen ist.

2- Dieser Referenzpunkt einbauen in den Sketch.

Zu 2) Du benötigst je Schrittmotor einen Referenzsensor, also vier. In #81 sind die Referenzpunkte für Stunden und Minuten enthalten.

agmue:
Zu 2) Du benötigst je Schrittmotor einen Referenzsensor, also vier. In #81 sind die Referenzpunkte für Stunden und Minuten enthalten.

Sind das die beiden?

constexpr byte refMinPin = 3;
constexpr byte refStdPin = 4;

Dreht dann der Schrittmotor bis der refPin 3 resp. 4 durch den Sensor auf Ground gesetzt sind?
Falls dem so ist, müsste ich noch davor Kellerunter stellen, damit das Rad drehen kann und die Kellen nicht zerstört werden. Das gleiche dann bei den Stunden.

StepMinKelle.doSteps(512);
pinMode( refMinPin, INPUT_PULLUP );