Please help me with my stepper motor issue

Hey all,

I feel I am 90% there but am having an issue with the logic. Basically I would like to drive a pulley that will have a MPU6050 3 axis Gyro mounted to it to use as limit/feedback control for the stepper motor. I would like to drive the pulley roughly 180 degrees and then reverse 180 degrees and back and forth until power is cut. I realize cutting power will throw it off track but that is why I plan to use overshoot ranges and have it correct when out of both ranges.

My confusion is centered on the fact that I am telling the stepper motor to do two separate things over the same degree range. I had it “working” with an L298N driver but it was not consistent as it was current regulated and it got so hot it fried one of my L298Ns after a lot of experimentation. Here is the code for that setup.

#include<Wire.h>
#include <Stepper.h>

const int stepsPerRevolution = 200;  // change this to fit the number of steps per revolution
// for your motor

// initialize the stepper library on pins 8 through 11:
Stepper myStepper(stepsPerRevolution, 8, 9, 10, 11);

const int MPU_addr = 0x68;
int16_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ;

int minVal = 265;
int maxVal = 402;

const float MINz1 = 85;
const float MAXz1 = 95;
const float MINz2 = 265;
const float MAXz2 = 275;

//double x;
//double y;
double z;

void setup() {
  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);
  Wire.write(0);
  Wire.endTransmission(true);
  Serial.begin(9600);

  // set the speed at 60 rpm:
  myStepper.setSpeed(25);



}
void loop() {
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr, 14, true);
  AcX = Wire.read() << 8 | Wire.read();
  AcY = Wire.read() << 8 | Wire.read();
  AcZ = Wire.read() << 8 | Wire.read();
  int xAng = map(AcX, minVal, maxVal, -90, 90);
  int yAng = map(AcY, minVal, maxVal, -90, 90);
  int zAng = map(AcZ, minVal, maxVal, -90, 90);

  //x= RAD_TO_DEG * (atan2(-yAng, -zAng)+PI);
  //y= RAD_TO_DEG * (atan2(-xAng, -zAng)+PI);
  z = RAD_TO_DEG * (atan2(-yAng, -xAng) + PI);


  if (z > MINz1 and z < MAXz1) {
    // step one revolution  in one direction:
    Serial.println("clockwise");
    myStepper.step(800);
    delay(500);

  }
  if (z > MINz2 and z < MAXz2) {
    // step one revolution in the other direction:
    Serial.println("counterclockwise");
    myStepper.step(-800);
    delay(500);

  }

  else {
    Serial.println("Correcting");
    myStepper.step(25);
    delay(70);

  }
  //Serial.print("AngleX= ");
  //Serial.println(x);

  //Serial.print("AngleY= ");
  //Serial.println(y);

  Serial.print("AngleZ= ");
  Serial.println(z);
  //Serial.println("-----------------------------------------");


  delay(400);
}


I commented out calculations for x and y degrees as I am only using z in this project.

This worked as the program goes “blind” under the 800 and -800 steps routines so as long as it is calibrated in number of steps/speed so that it doesn’t get past either reversal range after the commanded steps are completed it will reverse and do another 800 steps or so in the opposite direction. I used the else statement (with the serial print correcting) as if it should get out of range it will correct until it reaches the first direction range.

With the TB6600, which is amazing in comparison to the L298n, I am having a difficult time making it do the “blind” sequence of steps as before so that it will continue in the assigned direction once hitting one of the target ranges even though they are contradictory. As it is currently written, as soon as it crosses the range threshold, it reverses which makes sense as that is what I am telling it to do and it is not going “blind”.
I tried using a for statement like the one you see made commented out below (with the i++) language) in other previous iterations but it would only follow the first range and ignore anything else as having an if and a for and an else was problematic. Here is where I am at with that program.

/* Example sketch to control a stepper motor with TB6600 stepper motor driver and Arduino without a library: continuous rotation. More info: https://www.makerguides.com */
// Define stepper motor connections:
#include<Wire.h>

#define dirPin 2
#define stepPin 3

const int MPU_addr = 0x68;
int16_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ;

int minVal = 265;
int maxVal = 402;

const float MINz1 = 89;
const float MAXz1 = 280;


//double x;
//double y;
double z;

void setup() {
  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);
  Wire.write(0);
  Wire.endTransmission(true);
  Serial.begin(9600);

  // Declare pins as output:
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);

}
void loop() {
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr, 14, true);
  AcX = Wire.read() << 8 | Wire.read();
  AcY = Wire.read() << 8 | Wire.read();
  AcZ = Wire.read() << 8 | Wire.read();
  int xAng = map(AcX, minVal, maxVal, -90, 90);
  int yAng = map(AcY, minVal, maxVal, -90, 90);
  int zAng = map(AcZ, minVal, maxVal, -90, 90);

  //x= RAD_TO_DEG * (atan2(-yAng, -zAng)+PI);
  //y= RAD_TO_DEG * (atan2(-xAng, -zAng)+PI);
  z = RAD_TO_DEG * (atan2(-yAng, -xAng) + PI);
  Serial.print("Z= ");
  Serial.println(z);

  if (z > MINz1 and z < MAXz1) {
    // step one revolution  in one direction:
    Serial.println("CCW");
    // Set the spinning direction clockwise:
    digitalWrite(dirPin, LOW);
    // Spin the stepper motor 1 revolution slowly:
    // for  (int i = 0; i < 20; i++)
    // These four lines result in 1 step:
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(500);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(500);
  }


  else {
    Serial.println("CW");
    // Set the spinning direction clockwise:
    digitalWrite(dirPin, HIGH);
    // Spin the stepper motor 1 revolution slowly:
    // for  (int i = 0; i < 20; i++)
    // These four lines result in 1 step:
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(500);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(500);
  }


  //Serial.print("AngleX= ");
  //Serial.println(x);

  //Serial.print("AngleY= ");
  //Serial.println(y);


  //Serial.println("-----------------------------------------");


}




I realize I am going to have to get a fan or heatsink or something as this thing got up to 260 degrees F before I realized I had the dip switches incorrect (doh). It runs about 150 normally.

Thank you so much in advance for your help or advice!

Hi, @hpfiend

To add code please click this link;

What is your hardware, can you please post links to your peripherals?

What model Arduino are you using?

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Thanks…Tom… :grinning: :+1: :coffee: :australia:

Oh, my bad. Here goes… I am using an arduino r3 uno with a NEMA 17 stepper motor of these specifications: https://www.omc-stepperonline.com/download/17HS10-0704S.pdf and a TB6600 driver I purchased here: https://www.amazon.com/gp/product/B07HHS14VQ/ref=ppx_yo_dt_b_asin_title_o02_s00?ie=UTF8&psc=1

I have the DIP switch settings set for whole steps and 0.5 current and 0.7PkCurrent (the lowest available) this motor is rated at 0.7amps. I have a smaller dimensional motor with more torque I plan to use once i get the code figured out so I don’t burn it up and it has a rated current of 1.0 amp which works better with the DIP setting of 1.0 and 1.2 peak.

. I am using a 13.0 volt 1800ma power supply to drive the motor and a GY-521 /mpu6050 3 axis accelerometer/gyroscope module I purchased here: https://www.amazon.com/gp/product/B07P5YZ7ZD/ref=ppx_yo_dt_b_asin_title_o09_s00?ie=UTF8&psc=1

When I was using the L298N I purchased it here: https://www.amazon.com/gp/product/B01M29YK5U/ref=ppx_yo_dt_b_asin_title_o06_s00?ie=UTF8&psc=1

Here is a sketch of my wiring…

OK, now

means “some random stepper motor with no specifications given”. If you actually have the matching driver for it and the correct power supply, that is fine.

It is possible that some people here recognise what a TB6600 driver is, but preferable if you give at least the Web page from where you purchased it. A GY-521 /mpu6050 is much more familiar but again, a Web link to the actual module would be nice.

Those two are the “peripherals”. :sunglasses:

Please read the link Tom provided and correct your first post to show the code in a usable fashion. Unless you do that we do not really know due to the way this new forum displays it, whether you have properly formatted it.


And with the proper code mark-up now, it seems that it **is** nicely formatted. :+1:

Thanks for the clarification. I have updated both posts to reflect your suggestions. I overlooked the link Tom provided and only read the text previously!

So you want it to pan back and forth between 89 and 280 degrees forever?

I’d set a state variable to remember which direction you are panning, and then switch it when it goes past a limit. For example:


...
int goingCW; // state variable to remember which way to turn the motor

setup(){
  ...
  goingCW = 1; // initially start going CW
  ...
}

loop(){
 ... {read sensor as normal and calculate z}

    if ( z < MINz1) { //  too far CW, change direction
       goingCW = 0;
    }
    if (z > MAXz1) { // too far CCW, change direction
      goingCW = 1;
    }

    digitalWrite(dirPin, goingCW ? HIGH : LOW );
  
    // Spin the stepper motor 1 revolution slowly:
    // for  (int i = 0; i < 20; i++)
    // These four lines result in 1 step:
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(500);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(500);

  }

Basically, this just keeps stepping, and when it gets too far CW or CCW, as measured by Z, it switches its goal to the other limit.

1 Like

Hi,
OPs stepper specs.


Tom… :grinning: :+1: :coffee: :australia:

Hi,
Driver specs;



Can you tell us the application for your project please?

Tom… :grinning: :+1: :coffee: :australia:

Wow! that makes a lot of sense. Thanks. Let me try that and get back to you. I will read into this state variable business and the ? and then colon code you used as I have never seen that and is obviously an elegant solution.

Tom,
I am replacing a synchronous ac motor that drives a projector that projects an image on a wall that rotates continuously in one direction. I am wanting to only have the image move in a 180 degree sweep as, to me, orientation is essential. It was near impossible to reverse the original motor so I am trying to use a stepper. The intense heat may prove problematic.

IT WORKS! It stutters a little bit at the directional changes but that is me rotating the sensor in my hand. Thanks so much!!!

Glad it works for you. I’m not sure why you’d get a stutter at the limits though–Once it makes a turn, I’d think it would have to do a lot of motion before it would meet the criteria to go back the other way.

Thinking of a process in terms of state variables can simplify lots of problems. The Finite State Machine | Majenko Technologies does a good writeup.

Hi,

Does the code STOP the motor BEFORE changing direction?
How long is the STOP?
The inertia of the system could be working against the change in direction, causing extra load on the stepper and with the current limited the stepper may have a weak couple of steps.
Hence the stutter.

Just a thought… Tom… :grinning: :+1: :coffee: :australia:

Thank you Tom and DaveX,
No, the code does not have a delay programmed into it to allow it to break inertia. I think I had a loose connection as it worked much better after I tightened the contact screw. I do need to add a delay as when it is in application there is quite a bit more inertia involved as it will be driving a load but when I tried it affected the rpm as the delays are what gives the pulse width to the step pin.

What is worse, I had a 50% chance of getting this right and when I inverted the motor in the application setup it was the wrong way and changing the initial state seems to have no bearing on direction. If I do not change the reversal states (1,0) and change the initial state from 0 to 1 and apply power to the stepper motor between the minimum and maximum limits it should change direction but it does NOT. Changing how it responds to direction once I reach the limit does work but without being able to change both it isn't working. Here is the code as I wrote it below. I have the serial.print commands on for testing but it does work more smoothly without them once I get it setup I will comment them out.

// Define stepper motor connections:
#include<Wire.h>

#define dirPin 2
#define stepPin 3

const int MPU_addr = 0x68;
int16_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ;

int minVal = 265;
int maxVal = 402;

const float MINz = 290;
const float MAXz = 325;

int goingCW; // state variable to remember which way to turn the motor

//double x;
//double y;
double z;

void setup() {
  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);
  Wire.write(0);
  Wire.endTransmission(true);
  Serial.begin(9600);

  // Declare pins as output:
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  goingCW = 1; // initially start going CW
}
void loop() {
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr, 14, true);
  AcX = Wire.read() << 8 | Wire.read();
  AcY = Wire.read() << 8 | Wire.read();
  AcZ = Wire.read() << 8 | Wire.read();
  int xAng = map(AcX, minVal, maxVal, -90, 90);
  int yAng = map(AcY, minVal, maxVal, -90, 90);
  int zAng = map(AcZ, minVal, maxVal, -90, 90);

  // x= RAD_TO_DEG * (atan2(-yAng, -zAng)+PI);
  // y= RAD_TO_DEG * (atan2(-xAng, -zAng)+PI);
  z = RAD_TO_DEG * (atan2(-yAng, -xAng) + PI);
  Serial.print("Z= ");
  Serial.println(z);

  if ( z < MINz) { //  too far CW, change direction
    goingCW = 1;
  }
  if (z > MAXz) { // too far CCW, change direction
    goingCW = 0;
  }

  digitalWrite(dirPin, goingCW ? LOW : HIGH );

  // Spin the stepper motor 1 revolution slowly:
  // for  (int i = 0; i < 20; i++)
  // These four lines result in 1 step:
  digitalWrite(stepPin, HIGH);
  delayMicroseconds(2000);
  digitalWrite(stepPin, LOW);
  delayMicroseconds(2000);

  // Serial.print("AngleX= ");
  //Serial.println(x);

  // Serial.print("AngleY= ");
  // Serial.println(y);


  // Serial.println("-----------------------------------------");


}

Thank you so much for your help!