? Coding for 1RPM constant, Nema 17 through A4988?

Hi there. HELP please

Coding for 1RPM constant, Nema 17 through A4988 ???? Numb brain and bum. got so far but no nearer.

Tried downloading and editing lots of codes. New to this.

Just need Arduino code to run a Nema 17 motor at 1RPM constant clockwise, through an A4988 driver. Wiring and system working fine, just can't find this code, or work out how to adapt other codes.
It's for an Astro tracker, worn out from endless attempts and starry skies going by and no chance to photograph..

Thanks so much if you can help.
Paul

Help the helpers by reading and following the instructions in the "How to get the best out of this forum" post, linked at the head of every forum category.

If you want someone to write code for you, post on the Jobs and Paid Collaborations forum section.

1 Like

Stepper basics.

Simple stepper code

Important. Has your driver's coil current limit adjustment been done?

Are you using microstepping?

How accurate does the 1 RPM need to be?

1 Like

Which one worked best? And what was the problem?

const unsigned int StepsPerRevolution = 200;
const byte StepPin = 2;
const byte DirectionPin = 3;

const unsigned long MillisecondsPerStep = (60ul * 1000ul) / StepsPerRevolution;

void setup()
{
  digitalWrite(StepPin, LOW);
  pinMode(StepPin, OUTPUT);

  digitalWrite(DirectionPin, LOW); // HIGH for other direction
  pinMode(DirectionPin, OUTPUT);
}

void loop()
{
  static unsigned long lastStepTime = 0;

  if (millis() - lastStepTime >= MillisecondsPerStep)
  {
    lastStepTime += MillisecondsPerStep;
    digitalWrite(StepPin, HIGH);
    digitalWrite(StepPin, LOW);
  }
}
1 Like

Hi groundFungus.
Thanks for getting back.

(Coil current limit) Not as far as I know, unless it's the little adjustment screw on A4988, that a couple of posts suggested turning to stop, clockwise initially, had opened it a turn maybe since then (when nothing caught fire). All seemd to run on downloaded codes well, nothing getting warm apart from the Nema.
Using microstepping would be good to reduce vibration.
The 1RPM needs to be pretty accurate (settle for as accurate as possible).

(I'd have opened a bottle of wine if i'd managed 1RPM full steps by any means, pretty pleased even that it ran and as I said without fire or smoke :slight_smile:

Cheers
Paul

Hi Dave.
Thanks for getting back.

Best one or one of them was that below:
I edited it to run clockwise constant, (This is the original) problem was the delay in microseconds seemed to be the only way to adjust speed, and smaller and larger numbers both ran quicker, which fried my brain.

Assume trying to use a hammer to undo a nut :slight_smile:

Thanks
Paul

const int dirPin = 2;
const int stepPin = 3;
const int stepsPerRevolution = 200;

void setup()
{
// Declare pins as Outputs
pinMode(stepPin, OUTPUT);
pinMode(dirPin, OUTPUT);
}
void loop()
{
// Set motor direction clockwise
digitalWrite(dirPin, HIGH);

// Spin motor slowly
for(int x = 0; x < stepsPerRevolution; x++)
{
digitalWrite(stepPin, HIGH);
delayMicroseconds(2000);
digitalWrite(stepPin, LOW);
delayMicroseconds(2000);
}
delay(1000); // Wait a second

// Set motor direction counterclockwise
digitalWrite(dirPin, LOW);

// Spin motor quickly
for(int x = 0; x < stepsPerRevolution; x++)
{
digitalWrite(stepPin, HIGH);
delayMicroseconds(1000);
digitalWrite(stepPin, LOW);
delayMicroseconds(1000);
}
delay(1000); // Wait a second
}

The A4988 driver only steps the motor on the positive going edge of the step signal. The duration of the high portion only needs to be high for 1 microsecond (see the data sheet). Then the time that the signal is low determines the speed of the motor. See Robin2's simple stepper code linked in post #3.

Using microstepping does more than just reduce the size of individual steps (increase resolution) and reduce vibration. It also reduces the effects of resonance. Resonance can be especially pesky on less damped drives like rack and pinion, lead screw or worm and sector.

Read the forum guidelines to see how to properly post code and some good information on making a good post.
Use the IDE autoformat tool (ctrl-t or Tools, Auto format) before posting code in code tags.

You can go back and fix your original post by highlighting the code and clicking the </> in the menu bar.
code tags new

2 Likes

This code gets me really close to 1 RPM as measured with a limit switch connected to the stepper and outputting to my logic analyzer. You can tune it for your system. Note the pin numbers have been changed to protect the innocent, er , to work with the CNC shield set up that I use to test stepper code. This is a non-blocking version so that you can do other stuff besides spin the motor. Just make sure to call the step() function very often (every time through a fast loop()).

Set the microstepping to x8 on the driver. That is MS1 = HIGH, MS2 = HIGH and MS3 = LOW (jumper in place is HIGH, jumper off is LOW).

const byte dirPin = 5;
const byte stepPin = 2;
const byte enablePin = 8; // for CNC shield
const byte limitPin = 3;

const int stepsPerRevolution = 1600; // 1/8 step x 200 steps / rev

void setup()
{
   // Declare pins as Outputs
   pinMode(stepPin, OUTPUT);
   pinMode(dirPin, OUTPUT);
   pinMode(enablePin, OUTPUT);
   digitalWrite(enablePin, LOW);
   pinMode(limitPin, INPUT_PULLUP);
}

void loop()
{
   // Set motor direction clockwise
   digitalWrite(dirPin, HIGH);
   step();
}

void step()
{
   static unsigned long timer = 0;
   unsigned long interval = 37500;
   if (micros() - timer >= interval)
   {
      timer = micros();
      digitalWrite(stepPin, HIGH);
      digitalWrite(stepPin, LOW);
   }
}

2 Likes

Thanks John.
I'd swapped Step Pin and Dir Pin, and it works. :slight_smile: .
Wasn't going to sort that out on my own. Symbols there not familiar with, and 'ul' I need to understand more.

Will pass good will on. Running Arduino File remains 'John Vasser...'
Great community on here. Have struggled to find answers for any stage of putting the electronics together up till now.

Thanks
Paul

Thanks groundFungus.
Going to try this. Have to put all this away and pack for weekend.
As with John Vasser, Thanks so much, will pass on good will. Now I'm getting somewhere, all this stuff is addictive again. Never played around with electronics much. Had built the 'Barn door' astro tracker around a 1RPM DC geared motor (too inacurate) up till now. Was within my understanding and ability to put together.
Have a good one.

Cheers
Paul

The math is fairly straightforward if you are careful with the units:

const int stepsPerRev = 200;
const float RPM = 1.0;

// us/step = 1000000UL us/sec *(60 sec/min)/ (1rev/min * 200 step/rev)
// 
unsigned long interval_us  = 1000000UL * 60 / RPM /200;  

Serial.print(interval_us);

// 1RPM prints:  300000

The 300000us is far above the delayMicroseconds(x) upper limit of x=16384:

... so changes on the order of 0.05RPM would wrap around for code using delayMicroseconds().

@groundFungus 's non-blocking code in #9 handles much longer intervals, and allows other things to be done at the same time.

My dad's 555-based telescope driver has a three-position momentary switch to control the speed. The center position was set to the to the tracking speed, and the two opposite momentary positions set to slew faster and slower than tracking speed. In your case, it would be like switching between 0.75RPM, 1.00RPM, and 1.50RPM, (or in us/step for a 200 step stepper, it is 400000us/step, 300000us/step, 200000us/step)

Higher microstepping ratios would be smoother and have proportionally smaller intervals.

Thanks DaveX.
I worked with both John Vasser's code and the one from groundFungus. Kept editing that, plus worked out the best power setting for the motor (little adjuster screw on A4988). Found a clear explanation of the use of and coding for MS pins too.
Have the motor running 1RPM (only 2 tenths of a second or so out in total, over 10minutes. Which is so much better , plus consistant! than I'd ever achieved) also running smoothly on 8th steps, and motor staying cool. Edited code to this below (including LED to show on/ready through project box)...
Will look in to other approaches to this and will have a go with yours.
Facinating the different approaches and coding possibilities.

Thanks
Paul

type or paste code here
```const byte dirPin = 2;
const byte stepPin = 3;

const int motMS1Pin = 4;
const int motMS2Pin = 5;
const int motMS3Pin = 6;
#define LED 13


const int stepsPerRevolution = 1600; // 1/8 step x 200 steps / rev
const int motSpeed=1;
void setup()
{
   // Declare pins as Outputs
   pinMode(stepPin, OUTPUT);
   pinMode(dirPin, OUTPUT);
   pinMode(motMS1Pin, OUTPUT);
   pinMode(motMS2Pin, OUTPUT);
   pinMode(motMS3Pin, OUTPUT);
   pinMode(LED, OUTPUT);
   
   

}

void loop()
{
   // Set motor direction clockwise
   digitalWrite(dirPin, LOW);
   step();
}

void step()
{
   static unsigned long timer = 0;
   unsigned long interval = 18737;
   if (micros() - timer >= interval)
   {
      timer = micros();
      digitalWrite(stepPin, HIGH);
      digitalWrite(stepPin, LOW);
      digitalWrite(motMS1Pin, HIGH);
      digitalWrite(motMS2Pin, HIGH);
      digitalWrite(motMS3Pin, HIGH);
      digitalWrite(LED, HIGH);
   }
}
1 Like

Thanks again groundFungus.
I worked with both John Vasser's code and yours. Kept editing yours, plus worked out the best power setting for the motor (little adjuster screw on A4988). Found a clear explanation of the use of and coding for MS pins too.
Have the motor running 1RPM (only 2 tenths of a second or so out in total, over 10minutes. Which is so much better , plus consistant! than I'd ever achieved) also running smoothly on 8th steps, and motor staying cool. Edited your code to this below (including LED to show on/ready through project box)...
Will look in to other approaches to this and will have a go with yours.
Facinating the different approaches and coding possibilities.

type or paste code here
```const byte dirPin = 2;
const byte stepPin = 3;

const int motMS1Pin = 4;
const int motMS2Pin = 5;
const int motMS3Pin = 6;
#define LED 13


const int stepsPerRevolution = 1600; // 1/8 step x 200 steps / rev
const int motSpeed=1;
void setup()
{
   // Declare pins as Outputs
   pinMode(stepPin, OUTPUT);
   pinMode(dirPin, OUTPUT);
   pinMode(motMS1Pin, OUTPUT);
   pinMode(motMS2Pin, OUTPUT);
   pinMode(motMS3Pin, OUTPUT);
   pinMode(LED, OUTPUT);
   
   

}

void loop()
{
   // Set motor direction clockwise
   digitalWrite(dirPin, LOW);
   step();
}

void step()
{
   static unsigned long timer = 0;
   unsigned long interval = 18737;
   if (micros() - timer >= interval)
   {
      timer = micros();
      digitalWrite(stepPin, HIGH);
      digitalWrite(stepPin, LOW);
      digitalWrite(motMS1Pin, HIGH);
      digitalWrite(motMS2Pin, HIGH);
      digitalWrite(motMS3Pin, HIGH);
      digitalWrite(LED, HIGH);
   }
}

Cheers. Paul

If it is consistently 0.2/600=0.0003333 off, you might try adjusting the interval a bit:

   unsigned long interval = 18737 * 6002/6000;
   // or
   unsigned long interval = 18737 * 5998/6000;

Many thanks for the question, and all the helpful replies. I'm building an isosceles barn door tracker, using Uno, 4988 60:1 worm and micros timer. Struggling for resolution, but these posts give me plenty to try before asking for assistance. What a great resource!

An isosceles barn door tracker? A "barn door" tracker would not use a worm wheel. Sounds like you are making an equatorial tracker so you would want the worm wheel to move at siderial rate (7.2921158579e-5 radians per second).

For an isosceles barn door tracker, you need the threaded rod between the two equal-length legs to move a changing rate to get the angle to change a fixed (siderial) rate. If that's what you want, here is an example I wrote of how to do that.

// Using a stepper to move an equilateral barn-door tracker at sidereal rate.
// Written by John Wasser, on May 23rd, 2020

const uint16_t StepsPerRevolution = 200;
const float MillimetersPerRevolution  = 1.0; // 1mm pitch threaded rod
const uint16_t RotationRadiusMM = 400;  // Distance from hinge to threaded rod.

// Some sidereal rate constants:
// Seconds per Sidereal Day: 86164.0905  (Astronomical constant from Wikipedia)
// Radians per Second 7.2921158579e-5  (2 Pi radians divided by Siderail Day in Seconds)
const unsigned long MillisecondsPerRadian = 13713441;

uint32_t TimeAtLastStep = 0;
uint32_t IntervalToNextStep = 0;
uint32_t CurrentSteps = 0;  // Barn Door Tracker starts closed.
float CurrentAngle = 0;

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

  TimeAtLastStep = millis();
}

void loop()
{
  uint32_t currentTime = millis();

  if (currentTime - TimeAtLastStep >= IntervalToNextStep)
  {
    TimeAtLastStep += IntervalToNextStep;
    
    // Code Here to Step Once
    digitalWrite(LED_BUILTIN, HIGH);
    delay(1);
    digitalWrite(LED_BUILTIN, LOW);
    
    CurrentSteps++;

    uint32_t nextStep = CurrentSteps + 1;
    float nextRodLengthMM = (nextStep * MillimetersPerRevolution) / (float)StepsPerRevolution;

    float nextAngle = asin(nextRodLengthMM / (2 * RotationRadiusMM)) * 2.0;
    float deltaAngle = nextAngle - CurrentAngle;

    IntervalToNextStep = MillisecondsPerRadian * deltaAngle;
    CurrentAngle = nextAngle;

    Serial.print(currentTime);
    Serial.print('\t');
    Serial.print(CurrentSteps);
    Serial.print('\t');
    Serial.println(IntervalToNextStep);
  }
}

Hi John
Many thanks for the very helpful coding . I'll go through it with Arduino reference to ensure I understand it fully. The tracker is a barn door type, with the design heavily borrowed from Barn Door Tracker (Enhanced) with Variable Speed Control for Sun etc. (Arduino UNO) - YouTube The lower end of the A2 threaded rod terminates in a 25mm stainless ball, accurately ground into a brass socket. The rod is driven by the worm wheel, and at the top, is threaded through a 20mm rod, free to rotate between the two larger bearings shown. These mount onto the top board. Consequently, the required motor RPM is of the order of 60. I wrote a very basic timer code to see what resolution I could get experimentally


at 1/16 microstep. An interval (between change of motor state) of 207 microseconds gave a rod rotation of 83.1sec, however an interval of 208microseconds jumped it to 84.74 seconds. Hence my concern over resolution. Ive enclosed a pic to show the hardware. Again many thanks for the bespoke code.
Regards Peter

Something seems to be off in the math somewhere.
83.10 / 0.000207 = 401449.27 steps per revolution
84.74 / 0.000208 = 407403.85 steps per revolution

The steps per revolution should be an integer and a constant.

thanks for prompt response John,
I'll check it, lots to look at.