Einzeigeruhr / 24 Stunden Wanduhr mit Stepper Motor

Hallo zusammen, ich versuche eine einfache Einzeigerwanduhr mit 24 Stunden Zifferblatt zu bauen (falls jemand sehen will was ich meinen https://www.uhren-sheriff.de/images/product_images/popup_images/24_stunden_einzeiger_uhr_von_botta.jpg). Dafür möchte ich einen 28-BYJ48 am ULN2003 Board mit der CheapStepper Bibliothek benutzen. Mit einem einfachen delay Sketch funktioniert das soweit auch halbwegs genau. Da ich aber gerne zusätzlich über einen NTP Server einmal am Tag (um 0 Uhr) die Zeit abfragen möchte (auch das geht schon) und dann den Zeiger per Hall Sensor auf 0 Stellung bringen will (keine Ahnung wie das funktionieren wird) kann ich kein delay verwenden.
Ich hab mich mal am “BlinkWithoutDelay” versucht, aber da mach ich irgendwas falsch. Ist sicher nur so ein typischer Anfängerfehler, aber da hänge ich jetzt schon ein paar Tage dran und hab rumprobiert.
Bei Versuchen gestern hat er einmal getickt aber das wars dann auch, den Sketch hab ich aber leider nicht gespeichert.

Ich wäre für jede Hilfe dankbar.

Der momentane Sketch sieht so aus:

#include <CheapStepper.h>

CheapStepper stepper (5,6,7,8);  

// Der Motor macht 4096 Stepps
// geteilt durch 4 weil einzelne Stepps nicht nötig sind macht 1024
// 86400 Sekunden (24Std.) /1024 = 4 Stepps alle 84,375 Sekunden

unsigned long alteZeit = 0;
const unsigned long ZeitbisTick = 1000; //zum testen 1000 //84375; //84,375 Sekunden


void setup()
{
  stepper.setRpm(12);
  stepper.newMoveCW(4);
}

void loop()
{
  unsigned long Zeit = millis();
  
  if (Zeit - alteZeit >= ZeitbisTick)
    {
    alteZeit = Zeit;
    stepper.run();
    }
 // stepper.off();
}

Gruß Rene

Ich kenne diese Bibliothek zwar nicht, aber stepper.run(); gehört ohne Bedingung nach loop und stepper.newMoveCW(4); hinter die Bedingung. Wäre mein Ansatz.

Hi

Eigentlich musst Du doch 'nur' alle paar Sekunden/Minuten den neuen Soll-Punkt ausgeben - den Rest macht die Lib durch Aufruf der .run() Methode.

Prüfe, ob Du einen 28BYJ-48 mit 4096 Steps/Umdrehung hast, oder ob Du ein ungerades Verhältnis davon hast - gibt mehrere Varianten dieser Stepper und in dem Einsatz-Bereich (wohl Lüftungsklappen) ist Das nicht weiter wichtig - die Stepper drehen ja irgendwann wieder zurück.
Bei einer Uhr ist Das eher selten der Fall :wink:

Lasse Deinen Stepper 10x hintereinander 4096 Schritte nach Plus gehen - wenn dann 12 Uhr immer noch 12 Uhr ist, hast Du ein 'gerades Getriebe'.

MfG

Ich glaube es sind nie genau 4096 Schritte pro Umdrehung. Es gibt nur Infos die genau den Wert angebenund andere die runden.
Laut In-Depth: Control 28BYJ-48 Stepper Motor with ULN2003 Driver & Arduino

sind das nicht 64:1 sondern 63.68395... :1 genaugesagt: (31322622)/(111099) = 567424/8910 = 25792/405 = 63.68395...
Ich verstehe auch nicht wieso eine Quelle von einem 32 Step/rev Motor spricht und die andere von einem 64 Step/rev.
Nehmen wir mal an 64 Schrittemotor, dann sind es stat 4096 nur ca 4075,77 Schritte pro Umdrehung also wenn Du ihn mit 4096 Schritten ansteuerst macht er ca 20,23 Schritte zuviel.

Du kannst die genauen Schritte Deines Motors auch einfach kontrollieren indem Du ihn 100 mal 4096 Schritte in eine Richtung machen läßt. Sind es genau 4096 Schritte wird er wieder auf der gleichen Stelle zum stehen kommen. Sind es aber 4075,77 dann hast Du nach 100x 4096 Scritte um ca 2022 Schritte zuviel gemacht (ca Halbe Umdrehung).

Du kannst um die Anzeige der Uhr sehr genau zu bekommen mit 4076 Schritten rechnen und mit einer Lichtschranke nach jeden Tag die Homeposition (zB in der Nacht um 3 oder 4) wieder Neu setzen. So ist die Anzeige für die Stunden genügend korrekt und driftet auch nicht während des Jahres ab.

Grüße Uwe

Hallo,

deine Frage geht ja eigentlich dahin wie Du den Motor fahren lassen kannst und bei einem Endschalter stoppen kannst. Du musst ihn immer nur ein Stück fahren lassen, dann den Endschalter abfragen, das machst Du so lange bis der Endschalter angefahren wurde. Das Ganze packst Du in eine Funktion (Reverenzfahren) und die rufst Du irgendwann dann halt mal auf.

Heinz

Hallo, vielen Dank für die Vorschläge und Tips !

Den Motor hab ich getestet, der scheint recht genau 4096 stepps zu haben, zumindest kommt er bei der Anzahl an Schritten am gleichen Punkt an wie er gestartet ist. Da es bei der Einzeigeruhr ja eh nicht um absolute Genauigkeit geht passt das soweit.

Mein Hauptproblem im Moment ist erst mal das ich ohne delay auskommen möchte. Mit folgendem sketch läuft die Uhr schon ganz gut, blockiert allerdings dem Ablauf.

#include <CheapStepper.h>

CheapStepper stepper (8,9,10,11);

void setup()
    {
    stepper.setRpm(12);
    }

void loop()
    {
    delay(84375);
    stepper.moveCW(4);
    stepper.off();
    }

Der Demoskecht für non-blocking moves sieht so aus :

/*
 * cheapStepper_newMoveTo.ino
 * ///////////////////////////////////////////
 * using CheapStepper Arduino library v.0.2.0
 * created by Tyler Henry, 7/2016
 * ///////////////////////////////////////////
 * 
 * This sketch illustrates the library's
 * "non-blocking" move functions -
 * i.e. you can perform moves with the stepper over time
 * while still running other code in your loop()
 * 
 * This can be useful if your Arduino is multi-tasking,
 * but be careful: if the other code in your loop()
 * slows down your Arduino, the stepper motor may
 * slow down or move with a stutter
 * 
 * //////////////////////////////////////////////////////
 */

// first, include the library :)

#include <CheapStepper.h>

// next, declare the stepper
// and connect pins 8,9,10,11 to IN1,IN2,IN3,IN4 on ULN2003 board

CheapStepper stepper (8,9,10,11);  


 // let's also create a boolean variable to save the direction of our rotation
 // and a timer variable to keep track of move times

bool moveClockwise = true;
unsigned long moveStartTime = 0; // this will save the time (millis()) when we started each new move


void setup() {

  // let's run the stepper at 12rpm (if using 5V power) - the default is ~16 rpm

  stepper.setRpm(12);

  // let's print out the RPM to make sure the setting worked
  
  Serial.begin(9600);
  Serial.print("stepper RPM: "); Serial.print(stepper.getRpm());
  Serial.println();

  // and let's print the delay time (in microseconds) between each step
  // the delay is based on the RPM setting:
  // it's how long the stepper will wait before each step

  Serial.print("stepper delay (micros): "); Serial.print(stepper.getDelay());
  Serial.println(); Serial.println();

  // now let's set up our first move...
  // let's move a half rotation from the start point

  stepper.newMoveTo(moveClockwise, 2048);
  /* this is the same as: 
   * stepper.newMoveToDegree(clockwise, 180);
   * because there are 4096 (default) steps in a full rotation
   */
  moveStartTime = millis(); // let's save the time at which we started this move
  
}

void loop() {

  // we need to call run() during loop() 
  // in order to keep the stepper moving
  // if we are using non-blocking moves
  
  stepper.run();

  ////////////////////////////////
  // now the stepper is moving, //
  // let's do some other stuff! //
  ////////////////////////////////

  // let's check how many steps are left in the current move:
  
  int stepsLeft = stepper.getStepsLeft();

  // if the current move is done...
  
  if (stepsLeft == 0){

    // let's print the position of the stepper to serial
    
    Serial.print("stepper position: "); Serial.print(stepper.getStep());
    Serial.println();

    // and now let's print the time the move took

    unsigned long timeTook = millis() - moveStartTime; // calculate time elapsed since move start
    Serial.print("move took (ms): "); Serial.print(timeTook);
    Serial.println(); Serial.println();
    
    // let's start a new move in the reverse direction
    
    moveClockwise = !moveClockwise; // reverse direction
    stepper.newMoveDegrees (moveClockwise, 180); // move 180 degrees from current position
    moveStartTime = millis(); // reset move start time

  }

}

ich schaffe es aber bisher nicht da mit millis eine Wartezeit von X Sekunden einzubinden.

Hi

Woher weiß die Lib, wie schnell die Takte für 12 Umdrehungen die Minute sein müssen?
Davon ab - wäre Es nicht sinnvoller, auf 1 Umdrehung/24h einzustellen?

Schritte pro 360°: 4096
Der Tag hat 24h a 60m a 60s a 1000ms
mach alle 21093,75ms einen Step

Das kann man auch zu Fuß ansteuern, zufällig ergibt die 3/4tel Millisekunde alle 4 Aufrufe eine gerade Millisekunde - und zufällig haben wir 4 Muster, Die der Motor haben will.

MfG

Warum benutzt Du nicht einfach die Stepper.h der IDE. Für deine Anwendung sollte die doch vollkoimmen ausreichen, und dann machst Du alle 21093750 Microsekunden einen Step:

#include <Stepper.h>

Stepper stepper (4096, 5,6,7,8);  

// Der Motor macht 4096 Stepps
// geteilt durch 4 weil einzelne Stepps nicht nötig sind macht 1024
// 86400 Sekunden (24Std.) /1024 = 4 Stepps alle 84,375 Sekunden
// oder eben alle 21093750 µs einen Step

unsigned long alteZeit = 0;
const unsigned long ZeitbisTick = 100000UL; //zum testen 100ms //21093750UL; //21,093750 Sekunden


void setup()
{
  //stepper.setSpeed(12);   // Da immer nur 1 Step ausgeführt wird, kann man das weglassen
}

void loop()
{
  unsigned long Zeit = micros();
  
  if (Zeit - alteZeit >= ZeitbisTick)
    {
    alteZeit = alteZeit+ZeitbisTick;
    stepper.step(1);
    }
}

... oder so ... einfach Mal bis zum Ende denken - kann so einfach sein :wink:

alle 21093750 Microsekunden einen Step:
Da wirst Du auf die Nase fallen.
Je nach Arduino Modell ist die Taktrate mehr oder weniger genau.
Im schlimmsten Fall:
Ein UNO mit Resonator hat einen Ganggenauigkeit von 0,5% also auf 24 h gerechnet sind das ca 7,2 Minuten.
Ein Arduino mit Quarz ist besser 100ppm: das sind ca 8 Sekunden.

Den Motor hab ich getestet, der scheint recht genau 4096 Stepps zu haben, zumindest kommt er bei der Anzahl an Schritten am gleichen Punkt an wie er gestartet ist. Da es bei der Einzeigeruhr ja eh nicht um absolute Genauigkeit geht passt das soweit.

Hast Du nur 1 Umdrehung probiert? Hast Du versucht auch mal 100 Umdrehungen zu drehen?

Grüße Uwe

Hi

Meine 28-BYJ48 sind 'durch die Bank' 1:4096, getestet.

Wenn man eine Uhr baut, muß man eh eine Uhr verbauen - also eine RTC aka DS3231 - der Arduino selber ist halt weder in diese Richtung entwickelt, noch war jemals die Intention in dieser Richtung, Den als Uhr-Ersatz nutzen zu wollen - dafür gibt's halt Uhrenquarze oder eben externe Uhren wir die RTCs.

Ich würde halt eine RTC anflanschen und mir von Dieser die Sekunden geben lassen - dann muß man 'nur' gucken, ob die nächste Schaltzeit (alle 21093750) mit der aktuell gekommenen Sekunde erreicht/übertroffen wird. (der Prüfwert wird um 1000000 erhöht).

Quasi ein eigenes micros() - nur eben von der Geschwindigkeit des Arduino unabhängig.

MfG

Da er das Ganze ja im nächsten Schritt noch mit einem NTP-Server synchronisieren will, dürfte die Ganggenauigkeit nicht so entscheidend sein. Bei einem Arduino mit Quarz wären es immerhin weniger als ein Schritt in 2 Tagen.

Vielen Dank für eure Hilfe. Die CheapStepper Lib hab ich genommen weil mir wichtig war den Motor zwischen den steps abschalten zu können und sie den Motor mit einer 8er Sequenz anspricht, nicht wie die Stepper Lib mit 4, hab gelesen der will das so und dachte das ist besser. Leider kann die Lib nicht weniger als 12 rpm. Der Motor hat ne menge Runden gedreht mit 4096 und kommt immer an der selben Stelle an, ein zweiter den ich davon habe nicht :-). Der Nano hat wahrscheinlich auch so viel Abweichung in der Zeit wie der Uno, oder ? Wollte es letztendlich aber eh mit dem NodeMCU betreiben wegen der NTP Abfrage und der Stromsparfunktion vom ESP. Sollte ja mit Akku laufen das Ganze. Ich hab es heute mal mit einem NEMA17 am DRV8825 versucht damit hat es so prima geklappt :

#define ENABLE 10
#define dirPin 9
#define stepPin 8
#define stepsPerRevolution 1600

unsigned long previousMillis = 0;
const unsigned long interval = 53970; // -30 wegen delay unten, 200 Steps = 432000, 400 Steps = 216000, 800 Steps = 108000, 1600 Steps = 54000, 3200 Steps = 27000

void setup()
{
  pinMode(ENABLE, OUTPUT);
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  digitalWrite(ENABLE, HIGH);
  digitalWrite(dirPin, HIGH);
}

void loop()
{
    unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= interval)
  {
    previousMillis = millis(); 
    digitalWrite(ENABLE, LOW);
    for (int i = 0; i < stepsPerRevolution/1600; i++)
    {
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(400);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(400);
    }
    delay(30); // ohne delay vor dem abschalten steppt er nicht richtig
    digitalWrite(ENABLE, HIGH);
  }
}

allerdings wird das dann wohl mit dem Akku nix werden und ist irgendwie mächtig übertrieben, den will ich lieber für andere Dinge aufheben.

Ich hab da gerade was im Netz gefunden das werde ich später mit dem Sketch von MicroBahner mal testen ob das geht.

#define NrOfMotorOutputs 4 //Anzahl der Ausgänge zur Ansteuerung des Motors 
//Motor Pins für Ausgänge 1-4 definieren
byte MotorOutputPin[NrOfMotorOutputs] = {8,9,10,11};
const byte MotorOff = 0;

void setup() {
 //Alle Pins als Ausgänge setzen und auf LOW schalten
for (byte i=0; i < NrOfMotorOutputs; i++)
{
pinMode((MotorOutputPin[i]), OUTPUT);
digitalWrite(MotorOutputPin[i], LOW);
}
...

Void loop (){
//Alle Pins als Ausgänge setzen und auf LOW schalten
for (byte i=0; i < NrOfMotorOutputs; i++)
{
digitalWrite(MotorOutputPin[i], LOW);
}
...

Ich hab da wohl noch einiges zu lernen, mit copy-paste aus Demo Sketches komm ich da nicht weit :slight_smile:

Viele Grüsse und Danke nochmal für eure Hilfe

rene_1976:
Der Nano hat wahrscheinlich auch so viel Abweichung in der Zeit wie der Uno, oder ?

Grundsätzlich ja. Bei meiner LED-Uhr habe ich Nano und DS3231 verwendet, das reicht mir in der Genauigkeit. Aber mit WLAN ist die genaue Zeit nicht weit.

rene_1976:
Ich hab da wohl noch einiges zu lernen, mit copy-paste aus Demo Sketches komm ich da nicht weit :slight_smile:

Ich lerne gerne an Hand fertiger Programmbeispiele. Ich mag Bibliotheken mit vielen Beispielen. Das entbindet einen aber nicht, die Beispiele soweit zu verstehen, daß man sie für eigene Projekte adaptieren kann.

C & P & Verstehen finde ich gut :slight_smile:

C & P & Verstehen finde ich gut

C&P, verstehen und nach eigenen Bedürfnissen ändern/anpassen.
Grüße Uwe