Dividing stepper motor to more than 8 position

I'm trying to get a stepper motor like this:


To be used in a spin the wheel-type of game where it has to point to a score accurately. I'm using a motor driver to got it work with Arduino, with this code that decides to rotate or stop with a push button while recording its angle position:

#include <ezButton.h>

const int stepPin = 5;
const int dirPin = 6;
const int enPin = 7;
unsigned int x = 0;
unsigned int degree;

float currentAngle = 0;
float angle = 0;
float stepPerAngle = 1.8;
float numstep;

//Button
ezButton PUSH(4);

void setup() {
  Serial.begin(9600);
  pinMode(stepPin,OUTPUT); 
  pinMode(dirPin,OUTPUT);
  pinMode(enPin,OUTPUT);
  digitalWrite(enPin,LOW);  
  //Button
  PUSH.setDebounceTime(50);
}

void loop() {
  PUSH.loop();
  int n;
  const int test[] = {
    0, 45, 90, 135, 180, 225, 270, 315
  };  
  while(true){
    PUSH.loop();
    Serial.println(degree);
    if(PUSH.isReleased()){
      break;
    }
  }
  while(true){
    PUSH.loop();
    angle+=45;
    x++;
    if(x==8){
      x=0;
    }
    if(PUSH.isReleased()){
      degree=test[x];
      break;
    }
    if( currentAngle != angle ){
      if( currentAngle < angle){
        digitalWrite(dirPin,LOW);
        n = angle - currentAngle;
        numstep = n / stepPerAngle;
      }
      else if(angle==360){
        angle=0;
        numstep = n / stepPerAngle;
      }        
      /*else if( currentAngle > angle){
        digitalWrite(dirPin,LOW);
        n = currentAngle - angle;
        if( angle == 0){
          n =currentAngle;
        }
        numstep = n / stepPerAngle;
      }*/
      for(int x = 0; x < numstep; x++) {
        digitalWrite(stepPin,HIGH);
        delayMicroseconds(1000);
        digitalWrite(stepPin,LOW);
        delayMicroseconds(1000);
      }
      currentAngle = angle;
    }
  }
}

When I divide it into 8 positions, the stepper motor works accurately. But the spin the wheel needs more than 8, 32. When I try to divide it by more than 8 (e.g. 12 or 16), it becomes inaccurate.

Are there any ways to correct that and make the stepper motor accurate for 32 positions? Like if the motor driver should be replaced with other device to control the stepper motor, or that there are better alternatives than stepper motor?

Have you tried using a library to control the motor ?

With the correct motor driver board you should be able to enable micro-stepping which will allow greater granularity of stepping

1 Like

Don't use an increment of the angle. Use positions. Look at position = posNumber / totalsteps * stetsPerRev.

Such a picture is completely useless. We need technical data of the motor. What driver are you using? And what is 'accurately'? Within 1degree? 1/10 degree? A schematic and a picture of your layout would be helpful too.
Using a library for the stepper like my MobaTools would be more easy.

1 Like

Not yet, it'd give different results then? (though dunno if there are Library examples that are used with motor drivers like below, or it can work with these as well?)

So it's not impossible that these type of correct motor driver (or you refer to other motor board?) can give accurate division for like 32 position? Because so far from what I know, the motor steppers only got accurate when it's controlled by arcade machine-default PCB that strongly holds the motor in 1 of the 32 positions.

Did you mean:
degree=test[x]; ?

Well yes, forgot to update it in my post here.

For this one, how it can be implemented in my code? So far, without incrementing, I'm doing it like this (accurate with 8 position, inaccurate with 12 position):

#include <ezButton.h>

const int stepPin = 5;
const int dirPin = 6;
const int enPin = 7;
unsigned int x = 0;
unsigned int degree;

float currentAngle = 0;
float pos = 0;
float stepPerAngle = 1.8;
float numstep;

//Button
ezButton PUSH(4);

void setup() {
  Serial.begin(9600);
  pinMode(stepPin,OUTPUT); 
  pinMode(dirPin,OUTPUT);
  pinMode(enPin,OUTPUT);
  digitalWrite(enPin,LOW);  
  //Button
  PUSH.setDebounceTime(50);
}

void loop() {
  PUSH.loop();
  /*const int test[] = {
    0, 45, 90, 135, 180, 225, 270, 315
  };*/
  const int test[] = {
    0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330
  };
  while(true){
    PUSH.loop();
    Serial.println(degree);
    if(PUSH.isReleased()){
      break;
    }
  }
  while(true){
    PUSH.loop();
    if(x<11){
      x++;
    }
    else{
      x=0;
    }
    pos=test[x];
    Degree(pos);
    if(PUSH.isReleased()){
      degree=pos;
      break;
    }
  }
}

void Degree(int angle){
  int n;
  if( currentAngle != angle ){
    if( currentAngle < angle){
      digitalWrite(dirPin,LOW);
      n = angle - currentAngle;
      numstep = n / stepPerAngle;
    }
    else if(angle==360){
      angle=0;
      numstep = n / stepPerAngle;
    }        
    /*else if( currentAngle > angle){
      digitalWrite(dirPin,LOW);
      n = currentAngle - angle;
      if( angle == 0){
        n =currentAngle;
      }
      numstep = n / stepPerAngle;
    }*/
    for(int x = 0; x < numstep; x++) {
      digitalWrite(stepPin,HIGH);
      delayMicroseconds(1000);
      digitalWrite(stepPin,LOW);
      delayMicroseconds(1000);
    }
    currentAngle = angle;
  }
}

You use a way of coding that I will not try to understand.
Use more of Serial.print of key variable, and serial monitor.

This looks dangerous to me. The compiler might think like this: an integer will have a value from an integer divided by a float.

Use absolut positions and not increment like You do in this code.

I have made, and I use a build that either works in angle mode or step mode == steps per turning the table 360 degrees. This controls a rotating table on a mill.

For Your situation I would use step mode, 8 steps, 12 steps, 13 steps, 17 steps.....

Because I have a working code I can post it if you want.

I guess I'd like to see that working code for reference.

Okey.

The code is built for future functions/modes not having any final code yet.
Look for the Step mode logic. Lots of micro step is used.

#include<arduino.h>

//I2C for LCD
#include <AccelStepper.h>
#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>

hd44780_I2Cexp mylcd; // declare lcd object: auto locate & config exapander chip

// LCD geometry
#define LCD_COLS 16
#define LCD_ROWS 2

boolean state, old_state;
byte mode = 0; //// 0 = Antal sidor, 1 = vinkel per steg
int faces = 4;
unsigned long nrOfStepsPerRev = 90L*200*4; // microstep 4

void setup() {
  Serial.begin(115200);

  //1Hz 90% dutycycle
  pinMode(9, OUTPUT);                               // Set digital pin 9 (D9) to an output
  TCCR1A = _BV(COM1A1) | _BV(COM1A0) | _BV(WGM11);  // Enable the PWM output OC1A on digital pins 9 and invert output
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS12);     // Set fast PWM and prescaler of 256 on timer 1
  ICR1 = 62499;                                     // Set the PWM frequency to 1Hz: 16MHz/(256 * 1Hz) - 1 = 62499
  OCR1A = 6249;                                     // Set the duty-cycle to 10%: 62499 / 10 = 6249

  pinMode(10, OUTPUT);                               // 1 Hz pulse output on digital pin 9 (D9)
  pinMode(13, OUTPUT); digitalWrite(13, LOW);// Make board LED go off
  delay(10);//allow pwm timers to start

  int status;

  status = mylcd.begin(LCD_COLS, LCD_ROWS);
  if (status) // non zero status means it was unsuccesful
  {
    status = -status; // convert negative status value to positive number

    // begin() failed so blink error code using the onboard LED if possible
    hd44780::fatalError(status); // does not return
  }
  mylcd.clear();

  // Print start message to the LCD
  mylcd.print("221101a Rotating");
  delay(5000);
  mylcd.print("Running");
  delay(1000);

}


// The loop function is called in an endless loop
void loop()
{
  boolean cmd = readButtons();// + - Go Chg mode

  if (( millis() % 1000 ) > 499 )
  {
    state = true;
  }
  else
  {
    state = false;
  }

  if( state) { //update twice per second;
    if( mode == 0)   //number of surfaces
    {

      switch (cmd)
      {
        case increase:
        {
          faces++;
          brake;
        }          
        case decrease:
        {
          faces--;
          if (faces < 1) faces = 1; //1 rotate one turn
          brake;
        }          
        case Chg:
        {
          mode = 1; //switch to angle mode
//          goFlag = false;// 
          brake;
        }          
       case Go:
        {
          AccelStepper(anglePos);
          anglePos += nrOfStepsPerRev * 360 / faces;
          brake;
        }          
        default:
        brake;
      }
      if(goFlag)
      {
        AccelStepper(anglePos);
      }
    }
    else if ( state == 1 )  // Angle per step
    {
      
    }
  }
}// End of loop

Problem is 200 steps is not evenly divisible by 12, one twelfth of 200 is 16 and two thirds (16.6667), you can't do 2/3 of a step.
You can do 16 steps (28.8°) or 17 steps (30.6°), but not 30° ( 1/12 of 360).

That's the reality. Approximations will take place but using the proper algorithm the position will not drift.
Using micro stepping the error can be made smaller. For my rotating table the resolution is 1 to 576 000 corresponding to, I think, 0.24 seconds.

BTW, my reference for that angle division code is from this:

I haven't found much reference for using stepper motor to stop at certain angle for now.

No video is needed to se the trouble with Your code. When not even the theory is correct no code can fix it.
You told about 8 or 12 stops per rev. 8 worked but not 12.
There will always be numbers of steps that will not precisely match a stepper motor.

I guess it works better now if I use steps rather than angle, so problem solved, thank you.

Thanks for telling us.

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