Schrittmotor auf Position stoppen?

Hallo,
Unzwar will ich versuchen, dass sich mein Schrittmotor nur zu einem bestimmten Winkel bewegt und dann dort stoppt. Ist für ein Teleskop damit es nicht 360 Grad durchdreht. Dabei sollen die Schritte gezählt werden. Diese werden dann in einen Winkel umgerechnet. Erreicht der Schrittmotor zb 160 oder -160 Grad soll er stoppen und sich nicht darüber hinausdrehen. Leider stoppt er nicht, wieso?


#include <SoftwareSerial.h>
#include <Stepper.h>

#define BT_RX_PIN 1
#define BT_TX_PIN 0

#define IN1 8
#define IN2 10
#define IN3 9
#define IN4 11

SoftwareSerial bluetooth(BT_RX_PIN, BT_TX_PIN);

int16_t stepCount = 0;
const uint16_t stepsPerRevolution = 2048;  

Stepper myStepper(stepsPerRevolution, IN1, IN2, IN3, IN4);
int8_t motorOn = 0;  


void setup() {
 
  Serial.begin(9600);
  
  bluetooth.begin(9600);

  myStepper.setSpeed(12);  

  myStepper.step(0);

  delay(1000);
}

void loop() {

  if (Serial.available()) {
 
    char dataToSend = Serial.read();

    Serial.println(dataToSend);

    
    bluetooth.write(dataToSend);
  }


 
  if (bluetooth.available()) {
    
    char command = bluetooth.read(); 
    
    if (command == '1') {
      motorOn = 1;  
    } 
    else if (command == '2') {
      motorOn = -1;  
    }
    else if (command == '0') {
      
      motorOn = 0;  
      delay(5);     
    }

    stepCount++;
    
    float angle = stepCount * 360.0 / stepsPerRevolution; 

    if (angle >= 160 && motorOn > 0) {
      motorOn = 0;
      myStepper.step(0);
    } else if (angle <= -160 && motorOn < 0) {
      motorOn = 0;
      myStepper.step(0);
    }
  }

  if (motorOn != 0) {                                      
    myStepper.step(motorOn * (stepsPerRevolution / 1024));
    }
}

empfängt das BT-Modul eine 1 oder eine 2 dreht er sich und bei einer 0 hört er auf. Bei den Winkeln gibt es nur irgendwelche Probleme.

Spicke deinen Code mit Debug-Zeilen Serial.println("wasauchimmer");, ann findest du was falsch läuft.

Hallo Philipp, Dein Problem liegt darin, dass Du die gefahrene Strecke (winkel) nicht mitzählst und überprüfst. darum weiss der Morot nicht wieviele Schritte er ausführen muss. Ich hab im moment nur grad keine eit, dir das in Deinen Code einzubauen, da ich jetzt Einkaufen gehen muss. aber ich kann, wenn Du möchtest, mir das nacher, wenn ich zurück bin , anschauen. Hab Dir da ja beim Grundgerüst, um den Stepper entsprechend der Befehle über BT zu steuern schon geholfen. Ich glaub , ich hab sogar noch ne Code-Version bei mir, die darauf ausgerichtet ist, nur eine bestimmte Strecke zu fahren und dann zu stoppen. hHabe die damals, geschrieben , als deine vorgabe noch war, dass der Stepper genau eine Umdrehung machen soll, und dann stoppen. ich schaue , wenn ich vom Einkauf zurück bin nach, ich hab das auf dem anderen Laptop drauf.

Samstag Nachmittag einkaufen gehen? Ist das Masochismus oder ist da bei Euch nicht die Hölle los?

Gruß Tommy

Danke das wäre nett

Hallo,
warum willst Du das so kompliziert machen. Schau dir mal das Beispiele "stepper_OneRevolution" aus der Lib an. das Zählen macht doch die Lib schon für Dich , Du musst doch nur festlegen wie viele Schritte der Motor drehen soll. Man kann da auch eine 1 einsetzten. :wink:

myStepper.step(AnzahlSchritte);

Die Stepper standard lib hat jetzt nur den Nachteil das sie den Programmablauf blokiert. D.h so lange sich der Motor dreht kannst Du Deine Schnittstelle nicht abfragen.

Um das zu lösen kannst Du eine andere Lib verwenden. Schau dir mal die MobaTools oder die AccelStepper Lib an. Die können das im Hintergrund ohne blokieren.

Kommt etwas drauf an, aber bei uns , in der schweiz in meiner Kleinstadt geht das noch. Zudem blieb mir nichts anderes übrig, ich hatte diese Woche wieder einen meiner autistischen Schübe und konnte deshalb nicht raus unter fremde Menschen. leider waren dadurch jetzt auch meine Essensvorräte aufgebraucht und da ich keinesfalls weiter an Gewicht verlieren darf (45kg bei 174CM Körpergrösse sind halt etwas grenzwertig) war es unabdingbar, dass ich mich dazu zwang, einkaufen zu gehen. Ich muss da manchmal dann abwäen welches das kleinere Übel ist : Fasten mit Risiko auf Kreislauf-zusammenbruch oder doch unter fremde Menschen gehen und mich dabei unwohl oder fehl am Platz fühlen.

Ja, ich hab das alles schon mit Philipp durchgearbeitet gehabt. und ich hab aus seiner ersten Problemstellung noch die Dateien in meinem Archiv, habe damals 2 Versionen gemacht und ihm diejenige geschickt, die seine damaligen Wünsche nach Rücksprache erfüllte. Da sich jetzt die anforderungen geändert haben, müssen wir auf die 2. Version wechseln, dann klappt das auch mit der stepper-lib. Ich bereite das nacher vor, sobald ich zuhause bin.

Ok, Philipp, ich ab Dir das nun so zusammengestrickt, dass der Stepper anhält, sobald er 160° gefahren ist. Falls Du die 160° dahingehend überwacht haben möchtest, dass sich der Motor Ausschliesslich zwischen 0 und 160° bewegen darf, benötigst du 1. einen referenz-schalter damit der Motor den 0-Punkt finden kann und wir müssten dann eine Routine schreiben, mit der sich der Motor bei programmstart erstmal referenziert und dann müsstem wir die aktuelle Position jeweils in ner Variablen speichern. Da musst Du mir aber erst genaueres dazu sagen, damit ich weiss, wohin die Reise geht, so dass ich Dir mit entsprechendem Code helfen kann.


```cpp
#include <SoftwareSerial.h>
#include <Stepper.h>

#define BT_RX_PIN 1
#define BT_TX_PIN 0

#define IN1 8
#define IN2 10
#define IN3 9
#define IN4 11

SoftwareSerial bluetooth(BT_RX_PIN, BT_TX_PIN);

int16_t stepCount = 0;
uint16_t maxAngle = 160;
const uint16_t stepsPerRevolution = 2048;  

Stepper myStepper(stepsPerRevolution, IN1, IN2, IN3, IN4);
int8_t motorOn = 0;  


void setup() {
 
  Serial.begin(9600);
  
  bluetooth.begin(9600);

  myStepper.setSpeed(12);  

  myStepper.step(0);

  delay(1000);
}

void loop() {

  if (Serial.available()) {
 
    char dataToSend = Serial.read();

    Serial.println(dataToSend);

    
    bluetooth.write(dataToSend);
  }



  if (bluetooth.available()) {
    
    char command = bluetooth.read(); 
    
    if (command == '1') {
      motorOn = 1;
      stepCount = 0;  
    } 
    else if (command == '2') {
      motorOn = -1;
      stepCount = 0;  
    }
    else if (command == '0') {
      
      motorOn = 0;
      stepCount = 0;  
      delay(5);     
    }

  
    
    float angle = stepCount * 360.0 / stepsPerRevolution; 
    

    if (angle >= maxAngle && motorOn != 0) { // beim überwachen des gefahrenen winkels ist es ausreichend nur positiv zu zählen.
    
      motorOn = 0;
      stepCount = 0;
      myStepper.step(0);
    } 
  }

  if (motorOn != 0) {                                      
    myStepper.step(motorOn * (stepsPerRevolution / 1024));
    stepCount+=2;   // da der Motor immer 2 steps ausführt, müssen auch immer 2 steps addiert werden, damit die Pos stimmt.
    }
}

Ich hab dir übrigens noch die Variable 'maxAngle' hinzu gefügt, die den Maximalen Drehwinkel enthält, so dass eine einfache Anpassung an Deine gegebenheiten möglich ist, ohne sich durch den gesamten Code wühlen zu müssen.
Ich hoffe, es hilft Dir weiter.

Also er soll seinen Ausgangspunkt bei 0 Grad haben und dann sich maximal so 50 Grad und -50 Grad davon drehen. Hat er die Punkte 50 bzw -50 Grad erreicht soll er sich nicht mehr darüber hinaus weiterdrehen können, auch wenn das BT Modul eine 1 oder 2 empfängt. Zurückdrehen sollte er sich allerdings schon drehen können.

Also in dem fall auch dann die maximal +/- 50° vom nullpunkt aus einhalten, wenn du z.B. bei +30° gestoppt hast und danach wieder einen strtbefehl in + Richtung gibst, richtig? Ich hoffe ich hab es so korrekt verstanden. Ich mache Dir das auf Sonntag Mittag hin fertig.

Entschuldig bitte Philipp, ich bin grad völlig kapput und muss mich etwas hinlegen. ich bin leider noch nicht ganz fertig mit deinem Code, und je nachdem wie lange ich jetzt schlafe (es ist jetzt 8:49 Uhr) kann es sein, dass ich es bis zur mittagszeit nicht ganz schaffe. ich mache aber sobald ich wieder bei Kräften bin weiter. viel fehlt nicht mehr, bitte Dich aber um ein bisschen Geduld.

Ja hast alles richtig verstanden. Und bitte ruhe dich so lange aus wie du brauchst. Mach dir bloß kein stress.

OK danke Philipp, bin jetzt wieder wach und mache nachmeinem Aufwach.Kaffee gleich weiter.

So philipp, Versuche es damit. Bitte lese Dir die Komentare , die ich Dir in den Code eingefügt habe, besonders auch in bBezug auf den Refernzschalter, sehr aufmerksam durch, damit Du das System korrekt ans Laufen bekommst. Wichtig, erst lesen und erst wenn Du die hardwar angepasst hast, hochladen. Wenn der Referenzschalter nicht richtig positioniert oder angeschlossen ist, kann das System so nicht funktionieren.


```cpp
#include <SoftwareSerial.h>
#include <Stepper.h>
#include <EEPROM.h>

#define BT_RX_PIN 1
#define BT_TX_PIN 0

#define REF_SW_PIN 6  // Referenzschalter an Pin 6 vom Arduino, der Schalter MUSS , wenn die 0-Position erreicht ist,
                      //  Pin & nach GND schalten !!! 
                      
#define IN1 8
#define IN2 10
#define IN3 9
#define IN4 11

SoftwareSerial bluetooth(BT_RX_PIN, BT_TX_PIN);
uint8_t blockNr = 0x00;
uint16_t blockAddr = 0x02;
uint16_t writeCount = 0x0000;
int16_t stepCount = 0;
uint16_t maxAngle = 50;  // maximaler verfahr-Winkel jeweils in + und - Richtung vom Referenz-Punkt aus
const uint16_t stepsPerRevolution = 2048;
float angle = 0.0;

Stepper myStepper(stepsPerRevolution, IN1, IN2, IN3, IN4);
int8_t motorOn = 0;
int8_t lastDir = 0;

void getEEProm() {
  if (EEPROM.read(0) == 0xC9) {           // EEPROM ist  initialisiert, lade Werte (lastDir, writeCount, blockNr) aus EEPROM
    blockNr = EEPROM.read(1);             // das eeprom wird in blöcke aufgeteilt um die lebensdauer des EEPROMS zu verlängern
    blockAddr = (blockNr * 3) + 0x02;     // berechnung der Startadresse des aktiven EEPROM-Blockes
    EEPROM.get(blockAddr, writeCount);    // dieser counter legt fest, wann der nächste Block aktiviert wird
    lastDir = EEPROM.read(blockAddr + 2); /* hier wird die letzte bewegungsrichtung aus dem EEprom geholt, zur feststellung
                                            in welche Richtung der Referenzschalter zu finden ist , dies ist bei nur einem, 
                                            in der Mitte des Weges angeordneten Referenzschalters notwendig, da der arduino 
                                            sonst nach dem Einschalten nicht feststellen kann in welche Richtung der Motor 
                                            laufen muss, um den Nullpunkt zu finden. 
                                            Wäre der Referenzschalter an einem Ende des Weges montiert kann die Nutzung des
                                            EEPROMS entfallen, und da der motor, egal wo er steht, den referenzschalter immer
                                            in die gleiche richtung suchen kann. Danach kann der Motor dann auch einfach in die
                                            Mitte des Weges (halbe Strecke) fahren und diesen Punkt als Ausgangspunkt annehmen, 
                                            also als Nullposition setzten. MCit nur in der Mitte montiertem 1 Referenzschalter 
                                            geht es auch, ist halt Programmtechnisch etwas aufwändiger, jedoch positionirt er so
                                            schneller, das heisst das Gerät ist nach dem Einschalten schneller betriebsbereit.
                                            Nachteil ist jedoch, dass das gerät nach dem hochladen des sketches, also bei der 
                                            ersten Inbetriebname entweder direkt auf dem Referenzschalter stehen muss, oder ein 
                                            wenig in negativer Richtung kurz vor dem Referenzschalter.
                                            UND ganz wichtig, im ausgerschalteten Zustand darf man nicht von Hand verstellen.                                            eine andere Möglichkeit wäre, anstelle des Referenzschalters, ein Poti eizubauen, 
                                            dessen achse mit dem bebegten Teil des Gerätes mitläuft, dann könnte der arduino 
                                            jederzeit die Position feststellen und auch kontrolieren, das ergäbe dann eine
                                            Art Servoantrieb , also ein antrieb mit aktiver Rückmeldung.
                                            */
    return;
  }
  // EEPROM noch nicht initialisiert, jetzt initialisieren.
  EEPROM.put(blockAddr, writeCount);
  EEPROM.write(1, blockNr);
  EEPROM.write(blockAddr + 2, (lastDir - 1));
  EEPROM.write(0, 0xC9);
}


void writeLastDirection() {
  if (lastDir == motorOn || motorOn == 0) return;  // nur schreiben wenn motor drehen soll und Richtung wechselt
  lastDir = motorOn;                               // drehrichtung in lastDir übernehmen
  writeCount++;                                    // schreibzähler erhöhen
  if (writeCount == 0) {                           // Zähler überlauf?  (>65535)
    blockNr++;                                     // ja, nächster EEPROM Block aktivieren.
    EEPROM.write(1, blockNr);                      // neue BlockNr ins EEPROM
    blockAddr = (blockNr * 3) + 0x02;              // neue Blockadresse berechmem.
  }
  EEPROM.put(blockAddr, writeCount);     // schreibzähler sichern
  EEPROM.write(blockAddr + 2, lastDir);  // Richtung im EEPROM sichern
}

void goToRefPoint() {
  uint16_t maxSteps = ((maxAngle * stepsPerRevolution) / 360);
  int8_t refDir = 0 - lastDir;
  for (uint16_t steps = 0; steps < maxSteps; steps += 2) {
    if (digitalRead(REF_SW_PIN) == 0) break;               // Referenz Pos bereits gefunden?
    myStepper.step(refDir * (stepsPerRevolution / 1024));  //
  }
  angle = 0.0;
}

void setup() {

  Serial.begin(9600);

  bluetooth.begin(9600);

  myStepper.setSpeed(12);

  myStepper.step(0);

  pinMode(REF_SW_PIN, INPUT_PULLUP);

  getEEProm();

  goToRefPoint();

  delay(1000);
}

void loop() {

  if (Serial.available()) {

    char dataToSend = Serial.read();

    Serial.println(dataToSend);


    bluetooth.write(dataToSend);
  }



  if (bluetooth.available()) {

    char command = bluetooth.read();

    if (command == '1' && angle < maxAngle) {
      motorOn = 1;
      writeLastDirection();   // Richtungswechsel sichern
    } else if (command == '2' && angle > -maxAngle) {
  
      motorOn = -1;
      writeLastDirection();   // Richtungswechsel sichern

    } else if (command == '0') {

      motorOn = 0;
      delay(5);
    }

  }

  if (motorOn != 0) {
    angle = stepCount * 360.0 / stepsPerRevolution;                                    // Winkel berechnen
    if (((angle <= -maxAngle) && motorOn < 0) || ((angle >= maxAngle) && motorOn > 0)) {  // überwachen des gefahrenen Winkels
      motorOn = 0;                                                     // bei erreichen einer der Endpos -> Motor aus
    }
    myStepper.step(motorOn * (stepsPerRevolution / 1024));
    stepCount += (motorOn * 2);  // Steps entsprechend Laufrichtung (Wert von motorOn) zählen.
    // da der Motor immer 2 steps ausführt, müssen auch immer 2 steps addiert/subtrahiert werden, damit die Pos stimmt.
  }
}

Warum nicht eine Library verwenden, die mit Winkeln umgehen kann?

#include <SoftwareSerial.h>
#include <MobaTools.h>

#define BT_RX_PIN 1
#define BT_TX_PIN 0

#define IN1 8
#define IN2 9
#define IN3 10
#define IN4 11

SoftwareSerial bluetooth(BT_RX_PIN, BT_TX_PIN);

const uint16_t stepsPerRevolution = 2048;

MoToStepper myStepper(stepsPerRevolution, FULLSTEP );

void setup() {

  Serial.begin(9600);

  bluetooth.begin(9600);

  myStepper.attach( IN1, IN2, IN3, IN4);
  myStepper.setSpeed(120);  // RPM*10

  delay(1000);
}

void loop() {

  if (Serial.available()) {

    char dataToSend = Serial.read();

    Serial.println(dataToSend);


    bluetooth.write(dataToSend);
  }



  if (bluetooth.available()) {

    char command = bluetooth.read();

    if (command == '1') {
      myStepper.write(160);
    }
    else if (command == '2') {
      myStepper.write(-160);
    }
    else if (command == '0') {

      myStepper.stop();
    }

  }
}

Fehlt nur noch die referenzierung.
[Edit] Fehler im Programm besetigt: STEPDIR->FULLSTEP

Wenn Dein Code benutzt wird , und der Stepper per bluetooth sagen wir erst mal um 30° gfahren und dann gestoppt wird, und später wieder in gleicher Richtung gestartet wird, und dabei das Ende von 50° erreicht, stoppt dann der Motor automatisch und lässt nur noch eine Bewegung in entgegengesetze Richtung zu, so wie es sich philipp wünscht?

Ja. myStepper.write(160) bedeutet 'Fahre zur Position 160° vom Nullpunkt' Dort wird der Stepper dann automatisch stoppen. Wenn Du ihn vorher anhältst, und dann den gleichen Befehl wieder ausführst, bedeutet es das gleich: Ziel ist die Position 160° vom Nullpunkt aus. Da wird er stehen bleiben, egal von wo aus er gestartet wurde. Das gleiche gilt dann auch für die Position -160° ( oder welcher Winkel auch immer ). Das ist ähnlich wie der .write-Befehl bei einem Servo. Nur dass es hier die Winkelbegrenzung (0...180) nicht gibt. Die Winkelangaben können eben auch negativ oder >360° sein. Solange Du keine anderen Zielangaben als die beiden Endwerte vorgibst, wird er auch nicht darüber hinaus fahren. 'Zwischenstops' bleiben jederzeit möglich. Spezielle Zwischenziele werden per Bluetooth ja nicht vorgegeben, soweit ich das verstehe. Wären grundsätlich aber auch möglich.
Wichtig für eine exakte Winkelbestimmung ist nur, dass die Angabe der Steps/Umdrehung ( beim Instanziieren des Steppers ) korrekt ist.

OK dankeschön für Deine ausführlichen erklärungen. Bieten die MoBaTools auch die Unterstützung, den Stepper zu referenzieren, also ne beliebige Home-Position festzulegen (hier am liebsten den Mittelpunkt als 0 Position/Home Position), sodass der stepper beim Programmstart automatisch die homePosition aufsucht, falls er noch nicht drauf steht? Oder müsste das noch seperat im sketch hinzu gefügt werden? Sorry die blöde Frage, ich hab bisher noch nicht mit den MoBaTools gearbeitet, weiss nur, dass es eine sehr mächtige und äusserst gut durchdachte lib von Dir ist. Ach ja, läuft die Lib auch auf ATTiny's (z.B. nem ATtiny 44)?

Da gibt es leider keinen Automatismus. Da gibt es auch zu viele verschiedene Möglichkeiten. Wobei der Mittelpunkt immer eine schlechte Variante ist, weil Du dann beim Start nicht weist, in welche Richtung der Stepper drehen muss um den Referenzpunkt zu erreichen. Der 'LimitSwitch' liegt daher eigentlich immer an einem Ende des Bewegungsbereiches. Allerdings muss das nicht unbedingt der Referenzpunkt für die spätere Bewegung sein. Den kannst Du beim Erreichen des Limit-Switches auch eine definierte Anzahl von Steps entfernt davon festlegen.
P.S. Es gibt in der Lib Stepper-Beispiele mit Referenzierung.

Die MobaTools laufen leider bei weitem nicht auf allen ATtinys. Voraussetzung ist zumindest, dass der ATtiny einen 16-bit Timer hat, was bei vielen schon nicht gegeben ist.