Moba Tools Stepper-Steuerung Geschwindigkeiten & Ramps

Ich arbeite gerade an einer Kameraslider-Motorisierung mittels Touchscreen und NEMA17+A4988 Stepper System. Ich verlinke ganz unten die bisherigen Threads zu diversen Fragen zum Projekt.

Hier der komplette Sketch in der aktuellen Fassung V2.0:

/* Slider Control V2 for 3.5" Parallel TFT-tft Shield Display with Adafruit_GFX
// Original by Mega-Testberichte.de - Marco Kleine-Albers
// Variant for Adafruit TFT & Moba Tools by gregorurabl.at - Gregor Urabl

//1,8°, 360/1,8 = 200
//200 steps is a full turn in fullstep mode
//use microstepping for smoother motion. see later in code

Standard TFT tft Pin Mappings:

*pin usage as follow:
*                  tft_CS  tft_CD  tft_WR  tft_RD  tft_RST  SD_SS  SD_DI  SD_DO  SD_SCK 
*     Arduino Uno    A3      A2      A1      A0      A4      10     11     12      13   
                         
*Arduino Mega2560    A3      A2      A1      A0      A4      10     11     12      13                           

*                  tft_D0  tft_D1  tft_D2  tft_D3  tft_D4  tft_D5  tft_D6  tft_D7  
*     Arduino Uno    8       9       2       3       4       5       6       7
*Arduino Mega2560    8       9       2       3       4       5       6       7 

*Remember to set the pins to suit your display module!
*/

#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_TFTLCD.h> // tft Library
#include <TouchScreen.h> // Touchscreen Library
#include <MCUFRIEND_kbv.h> // Touchscreen Hardware-specific library
#include <MobaTools.h> // Motor Control Library

// Colors
#define BLACK       0x0000
#define WHITE       0xFFFF
#define RED         0xF800
#define GREEN       0x07E0
#define BLUE        0x001F
#define ORANGE      0xFD20      
#define DARKCYAN    0x03EF      
#define DARKGREY    0x7BEF   
#define LIGHTGREY   0xC618     

//debug/msg
int x, y;
String msg="";
char text_buffer[80];

//////////////////////////////////
// Screen Setup
//////////////////////////////////

#define tft_CS A3 // Chip Select goes to Analog 3
#define tft_CD A2 // Command/Data goes to Analog 2
#define tft_WR A1 // tft Write goes to Analog 1
#define tft_RD A0 // tft Read gies to Analog 0

#define tft_RESET A4 // Can alternately just connect to Arduino's reset pin

// define pins for resistive touchscreen
#define YP A1 // must be an analog pin, use "an" notation!
#define XM A2 // must be an analog pin, use "an" notation!
#define YM 7 // can be a digital pin
#define XP 6 // can be a digital pin

// define touchscreen pressure points
#define MINPRESSURE 10
#define MAXPRESSURE 1000

// Define touchscreen parameters
// Use test sketch to refine if necessary
#define TS_MINX 930
#define TS_MAXX 130
 
#define TS_MINY 200
#define TS_MAXY 970
 
#define STATUS_X 10
#define STATUS_Y 65

MCUFRIEND_kbv tft; // Define object for TFT display
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300); // Define object for touchscreen / Last parameter is X-Y resistance, measure or use 300 if unsure

//////////////////////////////////
// Motor Setup
//////////////////////////////////

//motor pins
int ENABLE_PIN = 23;
int STEP_PIN = 25;
int DIR_PIN = 27;
int stepsPerRev = 3200; // 360°/1.8° = 200 Steps x 16 Microsteppint = 3200 Microsteps/Rev

//microstepping Pins M1 =A8, M2=A9, M3=A10
//TABELLE, Abschnitt Software: https://www.mega-testberichte.de/testbericht/einen-kameraslider-motorisieren-arduino-a4988-steppermotor-touchdisplay-do-it-yourself
//pin mappings 
int M1 = A8;
int M2 = A9;
int M3 = A10;

MoToStepper stepper (stepsPerRev, STEPDIR);
MoToTimer stepperPause;                    // Pause between stepper moves
short motorDirection = -1; // clockwise

//--basic values for movement
int speed_set = 70;
int raise_speed_by = 70;
int speed_max = 1400;
int distance_set = 5000;
int raise_distance_by = 1000;
int ramp_set = 0;
short ramp_steps_by = 100;
bool return_to_home = true;
bool manual_start_or_stop = true; // true == start, false = stop

//--needed for timelapse, buttons
int time_set = 0; // millisec
short raise_time_by = 100;
int time_max = 30000; // 30sec. For longer Delays change time_set & time_max in Code and increase data type to long. EG 900.000 = 15min

//--subdivisions for timelapse
int steps_set = 2; // Divider for Stops. So minimum drive half of the distance, stop, wait for delay, drive to end. Distnace between stops is distance_set/steps_set
short steps_max = 3200; // REPLACE ME WITH MAX STEPS BASED ON SLIDER LENGTH
short raise_steps_by = 1; //raised by steps

//--slider length in steps
long slider_length = 455000;  


//////////////////////////////////
//  Draw UI  
//////////////////////////////////

char buttonRadius = 10;
char buttonHeight = 60;
char buttonSpacing = 10;
uint16_t smallButtonWidth = 147;
uint16_t bigButtonWidth = 303;

// Define button array object
Adafruit_GFX_Button buttons[10];

  void drawButtons() {

// Create Buttons - initButton would create a Button from x and y coordinates in it's center. initButtonUL uses the top left corner
     buttons[0].initButtonUL(&tft, buttonSpacing, buttonSpacing, bigButtonWidth, buttonHeight, WHITE, DARKCYAN, WHITE, "NORMAL", 3); // NORMAL Button
        int speed_percentage = speed_set/14;
        int_to_string(speed_percentage, text_buffer); 
        sprintf(text_buffer,"%s%s",text_buffer,"%");
        buttons[1].initButtonUL(&tft, bigButtonWidth+2*buttonSpacing, buttonSpacing, smallButtonWidth, buttonHeight, WHITE, DARKGREY, WHITE, text_buffer, 3); // SPEED Button (set number)
     buttons[2].initButtonUL(&tft, buttonSpacing, buttonHeight+2*buttonSpacing, bigButtonWidth, buttonHeight, WHITE, RED, WHITE, "MANUAL", 3); // MANUAL Button
          int_to_string(distance_set, text_buffer); 
     buttons[3].initButtonUL(&tft, bigButtonWidth+2*buttonSpacing, 1*buttonHeight+2*buttonSpacing, smallButtonWidth, buttonHeight, WHITE, DARKGREY, WHITE, text_buffer, 3); // DIST Button (set number)
     buttons[4].initButtonUL(&tft, buttonSpacing, 2*buttonHeight+3*buttonSpacing, smallButtonWidth, buttonHeight, WHITE, DARKGREY, WHITE, "<-->", 3); // RETURN Button          
     buttons[5].initButtonUL(&tft, smallButtonWidth+2*buttonSpacing, 2*buttonHeight+3*buttonSpacing, smallButtonWidth, buttonHeight, WHITE, DARKGREY, WHITE,">>" , 3); // DIRECTION Button 
     int_to_string(ramp_set, text_buffer);  
     buttons[6].initButtonUL(&tft, 2*smallButtonWidth+3*buttonSpacing, 2*buttonHeight+3*buttonSpacing, smallButtonWidth, buttonHeight, WHITE, DARKGREY, WHITE, text_buffer, 3);// RAMP Button (set number)
     buttons[7].initButtonUL(&tft, buttonSpacing, 3*buttonHeight+4*buttonSpacing, smallButtonWidth, buttonHeight, WHITE, ORANGE, WHITE, "TIMELAP", 3);// TIMELAPSE START Button
          int_to_string(time_set*1000, text_buffer);  
     buttons[8].initButtonUL(&tft, smallButtonWidth+2*buttonSpacing, 3*buttonHeight+4*buttonSpacing, smallButtonWidth, buttonHeight, WHITE, ORANGE, WHITE, text_buffer, 3);// DELAY Button (set number)
          int_to_string(steps_set, text_buffer);  
     buttons[9].initButtonUL(&tft, 2*smallButtonWidth+3*buttonSpacing, 3*buttonHeight+4*buttonSpacing, smallButtonWidth, buttonHeight, WHITE, ORANGE, WHITE, text_buffer, 3); // STEPS Button (set number)

// Draw Buttons
        for(uint8_t buttonCounter = 0; buttonCounter <= 9; buttonCounter++) 
        {
        buttons[buttonCounter].drawButton();
        }

// Draw Info Texts for set number Buttons

    tft.setTextSize(1);
    tft.setTextColor(WHITE); 

    tft.setCursor(bigButtonWidth+2*buttonSpacing+60,buttonSpacing+5);  tft.print("SPEED");
    tft.setCursor(bigButtonWidth+2*buttonSpacing+50,2*buttonSpacing+buttonHeight+5);  tft.print("DISTANCE");  
    tft.setCursor(bigButtonWidth+2*buttonSpacing+60,3*buttonSpacing+2*buttonHeight+5);  tft.print("RAMP");  
    tft.setCursor(bigButtonWidth+2*buttonSpacing+40,4*buttonSpacing+3*buttonHeight+5);  tft.print("SUBDIVISIONS");  
    tft.setCursor(smallButtonWidth+2*buttonSpacing+60,4*buttonSpacing+3*buttonHeight+5);  tft.print("DELAY");  

    tft.fillRect(0, tft.height()-30, tft.width(), 30, LIGHTGREY); // Infobar
}

/*************************
**  Required functions  **
*************************/
void setup() {  

tft.reset();

  // basic motor setup

  // Microstepping
  pinMode(M1, OUTPUT);
  pinMode(M2, OUTPUT);
  pinMode(M3, OUTPUT);

  //see table for step values: https://lastminuteengineers.com/a4988-stepper-motor-driver-arduino-tutorial/
  digitalWrite(M1, HIGH);
  digitalWrite(M2, HIGH);
  digitalWrite(M3, HIGH);
  
  stepper.attach( STEP_PIN, DIR_PIN );
  stepper.attachEnable (ENABLE_PIN,1,LOW); // Set Enable Pin and Turn of Motor per Default

  // Setup the Display
  tft.begin(tft.readID());
  tft.setRotation(1);
  tft.fillScreen(BLACK);

  //call function to draw our gui  
  drawButtons();
  
  Serial.begin(115200); 
  Serial.println("Starting Slider Control V2.0 by Gregor Urabl \r\n");  
  Serial.print("TFT size is "); Serial.print(tft.width()); Serial.print("x"); Serial.println(tft.height());
  
}

void loop() {

    //touchscreen
  uint16_t i;
  digitalWrite(13, HIGH);
  TSPoint p = ts.getPoint();
  digitalWrite(13, LOW);

  pinMode(XM, OUTPUT);
  pinMode(YP, OUTPUT);
  if (p.z > MINPRESSURE && p.z < MAXPRESSURE)
  {

    p.x = map(p.x, TS_MINX, TS_MAXX, tft.width(),0);
    p.y = map(p.y, TS_MINY, TS_MAXY, tft.height(),0);

      //debug
      //check the area where we drew the buton for touch
      /*
      Serial.print("Touch X is:");
      Serial.print(p.x);
      Serial.print("\r\n");

      Serial.print("Touch Y is:");
      Serial.print(p.y);
      Serial.print("\r\n");*/

      //--------------------NORMAL button 
      if ((p.y >= 10) && (p.x >= 40) && (p.y <= 220) && (p.x <= 100) ) { 
       
      digitalWrite(ENABLE_PIN, LOW); //motor on   

      //Set Basic Speed and Acceleration/Ramp of each Steppers at startup
      stepper.setMaxSpeed(speed_set); //
      stepper.setRampLen(ramp_set);   //
      stepper.setZero(0); //set current Position as 0 

      updateStr("Starting Normal Run");   
      tft.fillRect(0, tft.height()-35, tft.width(), 5, RED); // Create Red Progress Bar Basis

      stepper.moveTo(distance_set*10*motorDirection);

      while(stepper.stepsToDo() > 0){
        Serial.println(stepper.moving());
        tft.fillRect(0, tft.height()-35, tft.width()-(tft.width()/100*stepper.moving()), 5, GREEN); // Progress Bar   
      }

          if(return_to_home == true){
            Serial.println("Returning Home"); 
            stepper.moveTo(0);
                while(stepper.stepsToDo() > 0){ // Progress Bar for Home Run
                Serial.println(stepper.moving());
                tft.fillRect(tft.width(), tft.height()-35, -(tft.width()-(tft.width()/100*stepper.moving())), 5, BLUE); 
                }
              if(stepper.currentPosition() == 0){ // turn motor of after reaching Home
            tft.fillRect(0, tft.height()-35, tft.width(), 5, BLACK); // "Remove" Progress Bar
            digitalWrite(ENABLE_PIN, HIGH); //motor off
              }       
          } else{digitalWrite(ENABLE_PIN, HIGH);} //motor off 
      }

      //--------------------SPEED button with UPDATE of number
     if ((p.y >= 240) && (p.x >= 40) && (p.y <= 330) && (p.x <= 110) ) {   

        //one touch raises speed by X up to "speed_max"
        if(speed_set < speed_max) {
          speed_set = speed_set + raise_speed_by;  
        }
        else {
          speed_set = 70;
        }       

        int speed_percentage = speed_set/14;
        int_to_string(speed_percentage, text_buffer); 
        sprintf(text_buffer,"%s%s",text_buffer,"%");
        buttons[1].initButtonUL(&tft, bigButtonWidth+2*buttonSpacing, buttonSpacing, smallButtonWidth, buttonHeight, WHITE, DARKGREY, WHITE, text_buffer, 3); // SPEED Button (set number)
        redrawButtons(1,bigButtonWidth+2*buttonSpacing+60,buttonSpacing+5,"SPEED");

        String speedMsg = "Speed set to ";
        speedMsg.concat(text_buffer);
        updateStr(speedMsg);
     }

      //--------------------MANUAL button
      if ((p.y >= 10) && (p.x >= 140) && (p.y <= 220) && (p.x <= 210) ) { 
 
      //Set Basic Speed and Acceleration/Ramp of each Steppers at startup
      stepper.setMaxSpeed(speed_set); //speed_set
      stepper.setRampLen(ramp_set);   //ramp_set

      if(manual_start_or_stop == true){ //start run on first click
      digitalWrite(ENABLE_PIN, LOW); //motor on   
      Serial.println(speed_set);
      stepper.setZero(0); //set current Position as 0 
      updateStr("Starting Manual Run");
      stepper.rotate(motorDirection);
      manual_start_or_stop = !manual_start_or_stop;
      }
      else{ // stop run on second click
        updateStr("Stopping Manual Run");
        manual_start_or_stop = !manual_start_or_stop;
          if(return_to_home == true){
            Serial.println("Returning Home"); 
            stepper.moveTo(0);
              if(stepper.currentPosition() == 0){ // turn motor of after reaching Home
            digitalWrite(ENABLE_PIN, HIGH); //motor off
              }       
          } else{digitalWrite(ENABLE_PIN, HIGH);} //motor off 
          }
      }

      //--------------------DISTANCE button with UPDATE of number
     if ((p.y >= 235) && (p.x >= 140) && (p.y <= 320) && (p.x <= 220) ) {   

        //one touch raises Distance by X up to Slider Length
        if(distance_set < slider_length) {
          distance_set = distance_set + raise_distance_by;  
        }
        else {
          distance_set = 0;
        }   

      int_to_string(distance_set, text_buffer); 
      buttons[3].initButtonUL(&tft, bigButtonWidth+2*buttonSpacing, 1*buttonHeight+2*buttonSpacing, smallButtonWidth, buttonHeight, WHITE, DARKGREY, WHITE, text_buffer, 3); // DIST Button (set number)
      redrawButtons(3,bigButtonWidth+2*buttonSpacing+50,2*buttonSpacing+buttonHeight+5,"DISTANCE"); 

        String distMsg = "Travel Distance set to ";
        distMsg.concat(text_buffer);
        updateStr(distMsg);
     }

      //--------------------RETURN/ NO RETURN button
      if ((p.y >= 10) && (p.x >= 250) && (p.y <= 100) && (p.x <= 310) ) {   

      return_to_home = !return_to_home;

      if(return_to_home == true){
         buttons[4].initButtonUL(&tft, buttonSpacing, 2*buttonHeight+3*buttonSpacing, smallButtonWidth, buttonHeight, WHITE, DARKGREY, WHITE, "<-->", 3);
         buttons[4].drawButton();
         updateStr("Return Mode set to RETURN"); 
        } else{
         buttons[4].initButtonUL(&tft, buttonSpacing, 2*buttonHeight+3*buttonSpacing, smallButtonWidth, buttonHeight, WHITE, DARKGREY, WHITE, "-->|", 3);
         buttons[4].drawButton();
         updateStr("Return Mode set to SINGLE RUN");
        }

        }

      //--------------------DIRECTION button
      if ((p.y >= 125) && (p.x >= 245) && (p.y <=220) && (p.x <= 315) ) {   
        
        motorDirection *= -1; // invert it

        if(motorDirection == -1){
         buttons[5].initButtonUL(&tft, smallButtonWidth+2*buttonSpacing, 2*buttonHeight+3*buttonSpacing, smallButtonWidth, buttonHeight, WHITE, DARKGREY, WHITE,">>" , 3); // DIRECTION Button 
         buttons[5].drawButton();
         updateStr("Direction set set to "); tft.write(0x10);tft.write(0x10);tft.write(" CW");
        } else{
         buttons[5].initButtonUL(&tft, smallButtonWidth+2*buttonSpacing, 2*buttonHeight+3*buttonSpacing, smallButtonWidth, buttonHeight, WHITE, DARKGREY, WHITE,"<<" , 3); // DIRECTION Button 
         buttons[5].drawButton();
         updateStr("Direction set set to "); tft.write(0x11);tft.write(0x11);tft.write(" CCW");
        }
      
      }

      //--------------------RAMP button with UPDATE of number
      if ((p.y >= 235) && (p.x >= 250) && (p.y <=325) && (p.x <= 320) ) {
        //one touch raises Ramp by X up to (slider_length/2)
        if(ramp_set < speed_max) { 
          ramp_set = ramp_set + ramp_steps_by;  
        }
        else {
          ramp_set = 0;
        }        

        int_to_string(ramp_set, text_buffer); 
        buttons[6].initButtonUL(&tft, 2*smallButtonWidth+3*buttonSpacing, 2*buttonHeight+3*buttonSpacing, smallButtonWidth, buttonHeight, WHITE, DARKGREY, WHITE, text_buffer, 3);// RAMP Button (set number)
        redrawButtons(6,bigButtonWidth+2*buttonSpacing+60,3*buttonSpacing+2*buttonHeight+5,"RAMP");

        String rampMsg = "Ramp set to ";
        rampMsg.concat(text_buffer);
        updateStr(rampMsg);

      }      

      //------------------------TIMELAPSE START button 
      if ((p.y >= 10) && (p.x >= 350) && (p.y <= 100) && (p.x <= 420) ) {          
      updateStr("Starting Timelapse with ");
      // TODO
      }     

      //------------------------DELAY button with UPDATE of number
      if ((p.y >= 125) && (p.x >= 360) && (p.y <= 220) && (p.x <= 420) ) {    // if ((p.y >= 125) && (p.x >= 360) && (p.y <= 220) && (p.x <= 420) ) { 

        //one touch raises time by X up to time_max
        if(time_set < time_max) {
          time_set = time_set + raise_time_by;  
          
        }
        else {
          time_set = 0;
        }             

        int_to_string(time_set, text_buffer); 
        buttons[8].initButtonUL(&tft, smallButtonWidth+2*buttonSpacing, 3*buttonHeight+4*buttonSpacing, smallButtonWidth, buttonHeight, WHITE, ORANGE, WHITE, text_buffer, 3);// DELAY Button (set number)    
        redrawButtons(8,smallButtonWidth+2*buttonSpacing+60,4*buttonSpacing+3*buttonHeight+5,"DELAY"); 

        String timeMsg = "Delay for Timelapse set to ";
        timeMsg.concat(text_buffer);
        updateStr(timeMsg);
      }      

       //------------------------STEPS button with UPDATE of number 
      if ((p.y >= 230) && (p.x >= 360) && (p.y <= 310) && (p.x <= 410) ) {    
        //one touch raises steps by 10 up to steps_max
        if(steps_set < steps_max) {
          steps_set = steps_set + raise_steps_by;  
        }
        else {
          steps_set = 100;
        }       

        int_to_string(steps_set, text_buffer); 
        buttons[9].initButtonUL(&tft, 2*smallButtonWidth+3*buttonSpacing, 3*buttonHeight+4*buttonSpacing, smallButtonWidth, buttonHeight, WHITE, ORANGE, WHITE, text_buffer, 3); // STEPS Button (set number)
        redrawButtons(9,bigButtonWidth+2*buttonSpacing+40,4*buttonSpacing+3*buttonHeight+5,"SUBDIVISIONS"); 

        String stepsMsg = "Steps set to ";
        stepsMsg.concat(text_buffer);
        updateStr(stepsMsg);
      }    
      
}
}

/***************************************************************************
 * update string so we see what has been touched - debug help
 ****************************************************************************/
void updateStr(String msg){ 
    tft.fillRect(0, tft.height()-30, tft.width(), 30, LIGHTGREY);
    tft.setCursor(20,tft.height()-22);
    tft.setTextSize(2);
    tft.setTextColor(BLACK); 
    tft.print(msg);    
    //Serial.println(msg); //Only for Debug
}

/***************************************************************************
 * redraw Buttons after Input
 ****************************************************************************/
void redrawButtons(char buttonNr,short xCursor,short yCursor, String buttonLabel){ 
    buttons[buttonNr].drawButton();
    tft.setTextSize(1);
    tft.setTextColor(WHITE); 
    tft.setCursor(xCursor, yCursor);
    tft.print(buttonLabel); 
}

/*****************************************************************************
 * convert X to String
 ****************************************************************************/
void int_to_string(int val, char* string) {
  if(string) 
     sprintf(string, "%d", val);  
  return;  
}

Im großen und Ganzen funktioniert das ganze bereits wie es sollte, allerdings haben sich bei der letzten Änderung von Accelstepper zu Moba Tools ein paar Fragen ergeben:

1) Geschwindigkeit

Ich habe in der Erstfassung des Sketches mit setSpeed gearbeitet, kam dabei aber aufgrund des Planetengetriebes meines Nema17 auf ziemlich hohe Werte die nicht sehr Userfreundlich einstellbar gewesen wären. Durch einen Multiplikator von ca. 200 auf die 200Steps/Rev vom Nema (mittels myStepper.currentPosition() über Serial Monitor ausgelesen), kam ich da auf ca 40.000 Steps pro Umdrehung. Ich nutze daher jetzt setMaxSpeed.

Um rauszubekommen, was ich dem Motor zumuten kann, habe ich mit einem der Demosketches von Moba Tools getestet und graduell den Wert erhöht.

/*  Example for MobaTools
    Moving a stepper back and forth
    https://raw.githubusercontent.com/MicroBahner/MobaTools/master/examples/_Stepper/back_ForthStepperPause/back_ForthStepperPause.ino
*/
#include <MobaTools.h>

// Adjust pins, steps and time as needed
const byte stepPin = 25;
const byte dirPin  = 27;
const int stepsPerRev = 3200;   // Steps per Revolution ( example with 800 Steps = 1/4 microsteps )
const long  targetPos = 6400;         // stepper moves between 0 and targetpos ,  example with 1600
long nextPos;

MoToStepper myStepper ( stepsPerRev, STEPDIR );
MoToTimer stepperPause;                    // Pause between stepper moves
bool stepperRunning;

void setup() {
  myStepper.attach( stepPin, dirPin );
  myStepper.setMaxSpeed( 1400 );  // 60 Rev/Min ( if stepsPerRev is set correctly )
  myStepper.setRampLen(0);
  stepperRunning = true;
}

void loop() {
  if ( stepperRunning ) {
    // Wait till stepper has reached target, then set pause time
    if ( !myStepper.moving() ) {
      // stepper has reached target, start pause
      stepperPause.setTime( 1000 );
      stepperRunning = false;
    }
  } else {
    // stepper doesn't move, wait till stepperPause time expires
    if ( stepperPause.expired() ) {
      // stepperPause time expired. Start stepper in opposite direction
      if ( nextPos == 0 ) {
        nextPos = targetPos;
      } else {
        nextPos = 0;
      }
      myStepper.moveTo( nextPos );
      stepperRunning = true;
    }
  }

  // The sketch is not blocked while the stepper is moving nor while it is stopped.
  // Other nonblocking  tasks can be added here
}

Bis 1400 funktionierts toll, ab dann wirds seltsam - teilweise wieder langsamer, teilweise Stillstand. Aber 1400 war ausreichend schnell, also sollte das mein Maximum werden. Daher ist in meinem Projekt die Max Speed jetzt 1400 und die Steps 70, also 5% jeweils.

Interessanterweise ist 1400 in meinem Sketch aber deutlich langsamer als im Demosketch. Kann das an der Speicherauslastung durch die vielen Grafiken etc. liegen? Am Loop sollts ja nicht liegen, genau wegen dieser Limitierung habe ich ursprünglich von Accelstepper auf Moba gewechselt. Wenn ich von 5% auf 100% steppe ist ein dementsprechend höheres Tempo erkennbar, nur kommt es bei weitem nicht an das ran, was 1400 beim Anderen war.

2) Ramp

Ich bin nicht sicher, ob ich den Ramp Parameter verstehe. Sehe ich das richtig, dass das eine lineare Rampe von Tempo 0 auf Tempo MaxSpeed darstellt, die über die Distanz der als Parameter mitgegebenen Steps läuft? Gibt es eine Möglichkeit das auch anders als linear, z.B. logarithmisch zu machen? Und gefühlt ruckelt die Beschleunigung stellenweise wärend der Ramp. Mache ich da was falsch im Code, oder ist das einfach so? Und was sind "gute" Ramp Erfahrungswerte?

3) Progress Bar

Ich lasse während des Normal Runs eine Progress Bar zeichnen. Diese funktioniert auch 1A, allerdings startet sie nicht bei Null sondern immer gleich mit einem "erledigten" Teil. Was kann da das Problem sein?

Also er startet nicht so:
[x------------------------------]
sondern so:
[xxxx--------------------------]

Danke schonmal im Voraus, sind wieder ne Menge Fragen :wink:

Und hier noch die angekündigte Thread Historie:
//////////////////////////////////////////////////////////////////

Amazing job!

I built a steppermotor-controlled turntable but just used a potentiometer to control direction and speed. Use it for shooting pix of items I put on eBay. I can remotely control the turntable while sitting at the computer capturing the photos.

To keep track of various similar items, I built an erasable slate. I kept black as a color to allow for small corrections.

Touch_shield_new_Only_painting_NO_Erase_NEW_Color_Order.ino (8.3 KB)

This uses an ELEGOO 2.8" TFT on an UNO R3





Was hat setSpeed bzw. setMaxSpeed mit den 40000 Steps/Umdr. zu tun? Die sind doch per HW vorgegeben, und ändern sich nicht durch die SW.

Auf dem Mega sollte setMaxSpeed() bis 2500 problemlos funktionieren. Du hast aber das Beschleunigen abgeschaltet:

Ich vermute mal, das da dein Motor bei höheren Stepraten Probleme bekommt.

Dein Sketch ist recht unübersichtlich, da kann ich das jetzt erstmal nicht nachvollziehen. Vielleicht hängt das auch mit deiner Rampenllänge zusammen.

        if(ramp_set < speed_max) { 
          ramp_set = ramp_set + ramp_steps_by;  

Was hat die Rampenlänge mit speed_max zu tun? Da sehe ich diesen direkten Zusammenhang nicht.

Grundsätzlich schon, es ist aber keine rein lineare Beschleunigung. Von 0 an ist es eher ein 'Softstart' und dann steigt die Beschleunigung an. Nach der in setRampLen() angegebenen Zahl von Steps hast Du deine Endgeschwindigkeit erreicht. Änderst Du die Geschwindigkeit während sich der Motor bereits dreht, ist die Rampe bis zur neuen Geschwindigkeit natürlich kürzer ( Da er sich schon dreht, hast Du sozusagen schon einen Teil der Rampe hinter dir ). Beim Bremsen dann umgekehrt mit 'SanftAuslauf'. Ungefähr so, wie Du vor der roten Ampel vor dem Stillstand wieder etwas von der Bremse gehst, damit es beim Anhalten nicht so 'ruckt'.
Die Charakteristik der Beschleunigung kannst Du nicht verändern.

Danach solltest Du in deinem Sketch die Finger vom Enable-pin lassen. Das Abschalten des Spulenstroms macht die Lib jetzt automatisch.

Da die MobaTools die step-pulse mit einem Timer-Interrupt erzeugen
der völlig unabhängig vom sonstigen Code arbeitet
kann das nur bedeuten, dass sich die Einstellungen der Anzahl Schritte pro Umdrehung zwischen Demo-Sketch und deinem Sketch unterscheiden.
oder
du falls du die Mikrostep-Steuer-Eingänge des A4988 benutzt, das es dort einen Unterschied gibt.

Da du eher langsame Drehzahlen brauchst würde ich dir empfehlen auf einen Stepper-Driver Typ TMC2209 umzusteigen. Damit läuft der Schrittmotor nahezu lautlos und zwar auch bei sehr kleinen Drehzahlen. Da wirst du gar kein Untersetzungegtriebe benötigen.
Und dann reichen vermutlich kleinere Step-Pulsfrequenzen aus.

Der TMC2209 macht intern immer 1/256 microstepping. Auch dann wenn man von außen Fullstep-Modus fährt. Daher ist der Motor so leise und läuft ruckelfrei auch bei niedrigen Drehzahlen.
Hier ist ein Demo-Video das das zeigt

@MicroBahner

Was hat setSpeed bzw. setMaxSpeed mit den 40000 Steps/Umdr. zu tun?

Naja, von der Zählweise U/min bzw. Steps/sec her hätt ich gemeint. Die U/Min waren da von den Werten her weniger zugänglich (zumindest für mich als Endanwender) als die Steps/sec.

Dein Sketch ist recht unübersichtlich, da kann ich das jetzt erstmal nicht nachvollziehen. Vielleicht hängt das auch mit deiner Rampenllänge zusammen.

Rampenlänge und Speed sind in beiden Sketches identisch, ich hab's auch mit verschiedenen Kombinationen in Beiden ausprobiert. Im Testsketch ist er immer schneller. Und es sollte auch ganz ohne Rampe (bzw. eben setRampLen(0)) funktionieren, oder?

Was hat die Rampenlänge mit speed_max zu tun? Da sehe ich diesen direkten Zusammenhang nicht.

Ja, das ist genau das was ich meinte mit ich verstehe die Ramps nicht so ganz :wink: Die Ramps müssten dann von der zu fahrenden Distanz abhängig sein, nicht vom Endtempo, korrekt? Also theoretisch statt speed_max dann distance/2 - dann würde er bis zur Hälfte langsam rauf beschleunigen und dann direkt wieder runter, korrekt?

Danach solltest Du in deinem Sketch die Finger vom Enable-pin lassen. Das Abschalten des Spulenstroms macht die Lib jetzt automatisch.

Perfekt, das wusste ich nicht. Dann werfe ich die ganze Ein- und Ausschalterei im Sketch raus.

@StefanL38 An sich sollten maxSpeed, RampLen sowie Microsteppings genau identisch sein, ich prüfe das aber nochmals nach. Genau deswegen kann ichs mir ja eben nicht erklären, an sich sollte es da keine Unterschiede abgesehen vom UI geben.

Bzgl TMC2209: Der wurde in einem der anderen Threads auch schon angesprochen, schaut nach einem tollen Teil aus. Ich schätze auch, dass ich in einem Mark2 Projekt den dann zum Einsatz bringe. Jetzt habe ich aber bereits den A4988 verkabelt, daher möchte ich da wenns geht jetzt nicht mehr hardwareseitig alles umbauen. Eventuell baue ich sogar das aktuelle Projekt in nem Jahr oder so auf den um, Platzmäßig scheint er bzgl.Gehäuse ja nicht großartig größer zu sein als der A4988

Ramp ist die Beschleunigungsrampe.
Wenn du eine ziemlich hohe Schrittgeschwindigkeit eingestellt hast
und als Anzahl Schritte für die Beschleunigungsrampe 1000 setzt

Wenn die Maximalgeschwindigkeit erst nach mehr als 500 Schritten erreicht wird
du aber mit Beschleunigung / Abbremsen nur 1000 Schritte fährst dann wird
500 Schritte lang beschleunigt und dann 500 Schritte abgebremst.

Genau so ist es. Wobei das Endtempo natürlich schon mit reinspielt, weil die Beschleunigung in der Rampe natürlich höher ist, wenn er innerhalb der zu fahrenden Distanz (Rampenlänge ) eine höhere Endgeschwindigkeit erreichen muss. Deshlab wird auch die Rampenlänge intern verändert, wenn Du nur die Geschwindigkeit veränderst, damit die Beschleunigung innerhalb der Rampe wieder etwa gleich ist. Wenn Du das nicht willst, kannst Du bei setSpeedSteps() auch immer gleich deine Rampenlänge mit angeben, dann wird die natürlcih berücksichtigt, und nicht verändert.
Hier ein kleiner Testsketch, um mit Speed un Rampe einwenig zu experimentieren, und das Ergebnis im Seriellen Plotter anzuzeigen. Da siehst Du auch sehr schön die Beschleunigungscharackteristik:

#include <MobaTools.h>
//Characteristik von Beschleunigen und Bremsen

constexpr int stepPerRev=800;
// you can experiment with speed and ramplen
constexpr int maxSpeed=12000;
constexpr int rampLen=400; 
long amplitude = 4*rampLen; // mit linearbewegung zwischen Beschleunigungs und Bremsrampe
//long amplitude = 2*rampLen; // Direkter Übergang von Beschleunigungs- zur Bremsrampe

MoToStepper myStepper(stepPerRev, STEPDIR);
MoToTimebase printTime;

void setup() {
  myStepper.attach(6,5);
  myStepper.setSpeedSteps(maxSpeed,rampLen); // set Speed and ramplength
  printTime.setBasetime( 20 );
  Serial.begin(115200);
  
}

void loop() {
  if ( printTime.tick() ) {
    Serial.println( myStepper.readSteps() );
  }
  if ( !myStepper.moving() ) {
    amplitude = -amplitude;
    myStepper.move(amplitude);
  }
}

Das ergibt im Seriellen Plotter:

Sehr cool, vielen Dank für die nicht zu unterschätzende Mühe @MicroBahner sowohl für die Library Erstellung als auch den tollen Support!

Ich habe jetzt nach euren Inputs nochmals die beiden Sketches miteinander abgeglichen und ja, im Grunde hattet ihr recht - es liegt am Microstepping. Wenn ich

digitalWrite(M1, HIGH); digitalWrite(M2, HIGH); digitalWrite(M3, HIGH);

komplett rausnehme, rennt er genau so flott wie im Testsketch. Spannenderweise ergibt aber auch Low/Low/Low (Fullstep) die langsamere Geschwindigkeit. Erst die komplette Entfernung dieser Zuweisungen "befreit" den Motor. Für mich bedeutet das, dass ich mal die ganze hardware fertig baue, schaue wie laufruhig er ggf. ohne Microstepping ist und dann entscheide ob mir geringeres Tempo bei ruhigerem Lauf oder höheres Tempo ohne Microstepping wichtiger ist. Bzw. ob ich dann doch eventuell den anderen Treiber in Betracht ziehe :wink:

Edit: Mit einem *16 im MaxSpeed bei eingestelltem Microstepping ist er deutlich flotter, das habe ich übersehen. Immer noch langsamer als ohne Stepping Einstellungen, aber flotter.

Das liegt daran, dass Du mit dem *16 die maximale Geschwindigkeir für den Mega ( deutlich)
überschreitest. Das wird dann intern von der Lib auf das Maximum von 2500 Steps/sec begrenzt. Zwar schneller als deine 1400 Steps/sec, aber nicht wirklich *16.

Wenn Du nur einen Stepper hast, kannst Du die interne Begrenzung auch bis auf 5000 Steps/sec hochsetzen. Dazu musst Du in der MobaTools.h die definition der CYCLETIME

#elif defined ARDUINO_ARCH_AVR ////////////////////////////////////////////////////////
	//#define NO_TIMER3             // never use Timer 3
	#define CYCLETIME       200     // Min. irq-periode in us ( default is 200 ), 

auf 100 ändern:

#elif defined ARDUINO_ARCH_AVR ////////////////////////////////////////////////////////
	//#define NO_TIMER3             // never use Timer 3
	#define CYCLETIME       100     // Min. irq-periode in us ( default is 200 ), 

Kleiner solltest Du es allerdings nicht machen, das schafft der MEGA dann nicht mehr.

Sehr cool, vielen Dank! Habs jetzt angepasst und die Werte im Sketch dementsprechend auch angeglichen, läuft wirklich schön jetzt. Jetzt muss ich mal drauf warten, dass ich das Motor-Montageteil bekomme (3D Druck) und alles zusammensetzen, dann gibt's den ersten Testlauf.

Vorher würde ich einen deutlich schnelleren Prozessor testen.
Wie wäre es mit einem ESP32? Der macht 30000 steps / Sekunde.

Dem kannste auch gleich noch per WLAN ein browser-fähiges User-Interface geben.

Nur nichts überstürzen :wink: Das ganze ist erst mein zweites Arduino Projekt, ich bin mal froh, dass jetzt soweit alles läuft. Den Screen mit dem Mega zu verheiraten und der gelötete Stiftleistenaufbau für den A4988 hat mich schon ausreichend Nerven gekostet bisher :smiley: Aber ich behalte die Inputs für den ESP32 und den anderen Motortreiber definitiv für zukünftige Projekte im Hinterkopf, klingt nach toller Hardware!

Bzgl Webinterface: Das macht bei diesem konkreten Projekt wenig Sinn, weil das ganze Teil an Filmsets zum Einsatz kommt - der Arduino+Screen ist buchstäblich ne Fernbediehung für den motorgetriebenen Slider auf dem meine Kamera montiert ist. Da stehe ich physisch daneben, weil ich auch die Kamera im Blick haben muss. Da auf W-LAN Verbindung und (remote) Bedienung z.B. mittels Laptop zu setzen macht weniger Sinn als mit der "Fernbedienung" kabelgebunden per Hand. Aber z.B. für nen Remotehead (fernsteuerbare 3 Achsen Motorisierung z.B. auf nem Kamerakran) ist das definitiv interessant mit dem Webinterface. Da bin ich räumlich weit genug weg davon, dass das nützlich sein kann.

Der ESP32 kann sein ganz eigenes WLAN aufmachen und als der Accesspoint arbeiten.
Man braucht nur den ESP32 und ein Handy oder Tablet oder Laptop und dann läuft es.

Weitere Infrastruktur ist nicht notwendig.

ESP32_als_AccessPoint-----------WLAN---------------Handy

Feine Sache, gut zu wissen. Danke @StefanL38. Meine Bedenken konkret bzgl. WLAN waren aber weniger, dass ich Infrastruktur brauche. Meine Mikrofone, die Kamera und sogar meine Flugdrohne operieren ebenfalls auf 2.4Ghz bzw 5Ghz Band, die Kamera sogar dezidiert via eigenem WLAN Access Point. Mir geht's da eher darum, dass es nicht notwendig ist räumlich davon entfernt zu sein und Kabel nunmal einfach Kabel sind. Ist einfach stabiler und ausfallsicherer so :wink:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.