RPM measurement of stepper motor

Hi All
My first post. I read some of the info on first posts but if I missed something happy to go back and fill it in.
This is my first Arduino project, although I have done a bit with the old BASIC Stamp and BASICMicro chips years ago. I am an old guy. :slight_smile:
The project is to install a stepper motor as the power feed for a small milling machine, x-axis. The motor is a NEMA23. The controller is a TB6600, driven by a 24vdc supply. The Arduino clone is a Smraza UNO
I have it working well, I think, but wondering how to calculate or get the RPM of the stepper, if possible. I have noticed that the speed of the motor is affected by how much info I print to the LCD since I put that in the middle of the loop, so, perhaps I came about that the wrong way? That was not a surprise to me when I did it but not sure how else to do it more efficiently.

I would like to be able to know the RPM so that I can calculate and display the feed speed of the table. (.100" per revolution of the motor). I have read a bit about the interrupts so I think it is something to do with that, but at my current level, a little above my head. If that is where I need to look, happy to go research more if you can tell me I am in the right area. I had it running ok, although the control made the speed a bit jumpy at places in the rotation of the pot. I blamed it on the potentiometer but then I installed the stepper library and it seems better. However, reading through some of the documentation, it seems that library is designed for 4 wire control, not a stepper controller so I am confused a bit as to whether or not it actually smoothed out my motor control. I don't think it should have made a difference in the stepper motor. I did not use any of the library commands. One other thing I have noticed is that the motor is smoother at higher pulse settings on the controller but also slower top speed. I am hoping that by making the program more efficient, I can utilize the higher pulse settings, although for my application, 800 is good. The only reason for higher speeds is to "jog" the table to one end or the other for setup. You would not machine at that speed.


Thanks for reading the backstory.

I would appreciate it if you can lead me in the right direction to...
Display the data on the LCD so as not to affect my motor speed
Retrieve data to be able to calculate RPM or (pulses in a time frame)?
General input on my programming is welcome (if you are gentle :slight_smile: ) I am sure it is "clunky" and or inefficient but I am happy the motor is spinning with all of the controls working as expected.

Thank you

#include <LiquidCrystal.h>
#include <Stepper.h>

/*
   LCD RS pin to digital pin 12
   LCD Enable pin to digital pin 11
   LCD D4 pin to digital pin 4
   LCD D5 pin to digital pin 5
   LCD D6 pin to digital pin 6
   LCD D7 pin to digital pin 7
   LCD R/W pin to ground
   10K resistor:
   ends to +5V and ground
   wiper to LCD VO pin (pin 3 on display!)
  v   6600 motor controller set to 800
*/

int dirpinout = 3;             //direction pin out to controller
const int dir_in = A2;      //direction control pin in from switch
int dir = 1;                // sets table direction initially CW is table left
int enablepin = 9;             //enable pin out to controller
int enableout = 1;          //sets motor off to start
int pulse = 2;              // pulse out pin to controller
const int potin = A0;       //speed pot connected to pin A0
int speedpulse = 500;         // pulse delay for speed management
int runpin = A1;             //run button enables or disables motor
int runn = 0;  //sets runn to state of runpin (low to run)
int jogin = A3;             //jog switch in
int jogout = 0;             //jog out
const int limitpin = A4;     //limit switch input
const int limitled = 13;    // limit indicator connected to pin A5** may not be connected
int limit = 0;              //limit switch state

// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = 12, en = 11, d4 = 4, d5 = 5, d6 = 6, d7 = 7;

LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup() {

  //Serial.begin(9600);           // open the serial port at 9600 bps:
  pinMode(limitpin, INPUT_PULLUP);
  pinMode (dir_in, INPUT); // switch takes pin low, otherwise, pin is high due to pullup
  pinMode (limitled, OUTPUT);
  pinMode (enablepin, OUTPUT);    //high to enable
  pinMode (pulse, OUTPUT);
  pinMode(dirpinout, OUTPUT);        //high for clockwise ****check this
  pinMode(runpin, INPUT);    //Run button enables motor to run
  pinMode(jogin, INPUT);     //Jog button in
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
}

void loop() {
  limit = digitalRead(limitpin);      //one to run, zero if at limit
  digitalWrite(limitled, limit);      //lights indicator
  runn = digitalRead(runpin);         //zero to run, one to not run
  jogout = digitalRead(jogin);
  dir = digitalRead(dir_in);  //sets motor direction according to switch
  digitalWrite(dirpinout,dir);
 
  lcd.print("Speed___");
  lcd.print(15000-speedpulse);
  lcd.clear();

  if (limit + runn == 2)              // if run on and limit not reached, enable controller
  {
    digitalWrite(enablepin, LOW);
  }
  else if (limit + jogout == 2)
  {
    digitalWrite(enablepin, LOW);  //if jog on and limit not reached, enable controller
  }
  else {
    digitalWrite(enablepin, HIGH);  //disable controller
  }


  speedpulse = analogRead (potin);         //reads speed pot
  speedpulse = map (speedpulse, 0, 1023, 0, 15000); //converts pot to speed delays
  digitalWrite (pulse, HIGH);           //pulses motor
  delayMicroseconds(speedpulse);      //delay
  digitalWrite (pulse, LOW);          //pulses motor off
  delayMicroseconds(speedpulse);     //delay

  //Notes count pulse. 400 pulses = 1 revolution
  //count time elapsed since start of counting and convert to seconds for rpm calculation
  //calculate feed speed at 0.100"/revolution
}

If you focus on one question, what would it be?

Your program sends commands to take steps.

RPM = (steps per second) x (revolutions per step) x (60 seconds per minute).

I guess if I had to pick one, knowing the RPM would be the best. I see the post after this one addresses it.

A DC motor would be better for speed control. Steppers are good at precision positioning.

Take a look at blink without delay to show you how to use millis. Then you can update the LCD once a second (or whatever) rather than spamming it.

Your software "knows" the number of steps (and you should know the number of steps per revolution) and you can use millis() to calculate the time (or time difference) and from that you can find the speed (RPM).

Of course this calculation takes time and that can potentially "interrupt" or slow-down your steps so you may not want to check RPM too frequently...

But since the loop just runs through repeatedly, dependant on delays set by the pot and other duties like displaying data. But it sounds like I need to find a timer that is independant of the loop. And then install a counter in the loop? Which I thought might just add further delays.

I will do more reading. Thank you

millis() and micros() come to mind.

Yes, agree. That is how most are, except for CNC. And I had the stepper motor so thought it might be a good opportunity to finally learn how to use them. And they are more compact than a suitable DC Motor. So as much an opportunity to learn about the Arduino as I had one to use. And it keeps my options open for future CNC .

This will not help, if there is too much delay in loop(). Updating the lcd needs several ms.

@shawnrich : What RPM do you want to achieve? If it's not too much, you can try my MobaTools library. The MotoStepper class creates the step pulses in timer interrupts. So they will be created even while your LCD is updated.

On an UNO the MotoStepper class reaches up to 2500 steps/sec. With only one stepper this can be tuned up to 5000 steps/sec ( by changing a #define in MobaTools.h )

Wow, you all are great. I can't keep up with how fast the answers are coming but I will look into the milli functions and blinking the display. Yes, the RPM does not need to be updated very often, and only till it is set really. Once it is milling, it usually will not change too much unless the user realizes he is too slow or too fast. But once a second would probably be fine.

Thank you!

Thanks. Max RPM would be about, guessing, 80 RPM.... Again, this is only to speed the table to a position. Milling would usually be around a max of 10 rpm so 0.100"/minute? Depends on cut depth, material, cutter size, etc. as most of you probably know or can guess. It is a small machine too, Sieg X3. I will look into your library. Thanks

My mistake forgetting that. You're on the right track!
I've created a little CNC controller myself and use the same primitive way to handle the speed so far. Better would be to use the library Accelstepper.h contains commands for acceleration, speed and more.

Nonsense.

As has been pointed out, it's the duty of the program to directly control the RPM. Not with RPM as the basic unit, but with discrete steps. If knowing the RPM is important, be it only once per second, it's important to write the software so that it is simple to calculate the RPM.

If I had to add RPM measurement into an existing and working program, I'd try to add a step count, if there isn't one already. A global int that increases for each step in one direction and decreases in the opposite direction. A call to one function would return a value which is the step count minus previous call step count. Divided by present clock time minus previous call clock time. That would give the average speed between two calls, scalable to any unit, f.i. RPM.

Thank you for that! Best thing I have heard in a few days of fumbling around here. :smiley:

I was looking at Accelstepper this morning and wondering if it would make a difference. I will look again.

That gives me lots to look up. Thanks.

It will! Just never use delay in the code. loop must turn around quite fast. Each turn of loop must "kick" the accelstepper code to keep the stepper "flying".

I made some progress but now running in circles. My math or the syntax is incorrect and I cannot see it. Currently, the LCD is set to just show rpm, as calculated. But it slowly counts down (ie following a reset) from 20 to...? negative numbers. Depends on how long I watch it but it obviously is not what I hoped for. Any suggestions? I have not gotten to move the display part outside of the loop yet or the accelstepper, as previously suggested. I thought I might work on one thing at a time, so the RPM for now.

#include <LiquidCrystal.h>
#include <Stepper.h>

/*
   LCD RS pin to digital pin 12
   LCD Enable pin to digital pin 11
   LCD D4 pin to digital pin 4
   LCD D5 pin to digital pin 5
   LCD D6 pin to digital pin 6
   LCD D7 pin to digital pin 7
   LCD R/W pin to ground
   10K resistor:
   ends to +5V and ground
   wiper to LCD VO pin (pin 3 on display!)
  v   6600 motor controller set to 800
*/

int dirpinout = 3;             //direction pin out to controller
const int dir_in = A2;      //direction control pin in from switch
int dir = 1;                // sets table direction initially CW is table left
int enablepin = 9;             //enable pin out to controller
int enableout = 1;          //sets motor off to start
int pulse = 2;              // pulse out pin to controller
const int potin = A0;       //speed pot connected to pin A0
int speedpulse = 500;         // pulse delay for speed management
int runpin = A1;             //run button enables or disables motor
int runn = 0;  //sets runn to state of runpin (low to run)
int jogin = A3;             //jog switch in
int jogout = 0;             //jog out
const int limitpin = A4;     //limit switch input
const int limitled = 13;    // limit indicator connected to pin A5** may not be connected
int limit = 0;              //limit switch state
int tsrpm = 0;               //t1rpm is start of timer for rpm
int tfrpm = 0;               //tfrpm is stoptime for rpm count
int pulses = 0;                //pulse is counter for loop cycles to use for rpm
int rpm = 0;                 //rpm is, well, rpm
int const setpulse = 400;    //set to pulse setting on controller


// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to

const int rs = 12, en = 11, d4 = 4, d5 = 5, d6 = 6, d7 = 7;

LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup() {

  //Serial.begin(9600);           // open the serial port at 9600 bps:
  pinMode(limitpin, INPUT_PULLUP);
  pinMode (dir_in, INPUT); // switch takes pin low, otherwise, pin is high due to pullup
  pinMode (limitled, OUTPUT);
  pinMode (enablepin, OUTPUT);    //high to enable
  pinMode (pulse, OUTPUT);
  pinMode(dirpinout, OUTPUT);        //high for clockwise ****check this
  pinMode(runpin, INPUT);    //Run button enables motor to run
  pinMode(jogin, INPUT);     //Jog button in
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  pulses = 0;
}

void loop() {
  pulses++;

  if (pulses == 1)  {
    tsrpm = millis()- millis();
  }
   if (pulses == setpulse)
  { tfrpm = millis()- tsrpm;
    pulses = 0;
  }
  rpm = 60/ ((tfrpm - tsrpm)/1000);
  
  limit = digitalRead(limitpin);      //one to run, zero if at limit
  digitalWrite(limitled, limit);      //lights indicator
  runn = digitalRead(runpin);         //zero to run, one to not run
  jogout = digitalRead(jogin);
  dir = digitalRead(dir_in);  //sets motor direction according to switch
  digitalWrite(dirpinout, dir);

  lcd.clear();
  //lcd.print("RPM__");
 // lcd.print("____");
  lcd.print(rpm);
  delay(5);

  if (limit + runn == 2)              // if run on and limit not reached, enable controller
  {
    digitalWrite(enablepin, LOW);
  }
  else if (limit + jogout == 2)
  {
    digitalWrite(enablepin, LOW);  //if jog on and limit not reached, enable controller
  }
  else {
    digitalWrite(enablepin, HIGH);  //disable controller
  }


  speedpulse = analogRead (potin);         //reads speed pot
  speedpulse = map (speedpulse, 0, 1023, 0, 15000); //converts pot to speed delays
  digitalWrite (pulse, HIGH);           //pulses motor
  delayMicroseconds(speedpulse);      //delay
  digitalWrite (pulse, LOW);          //pulses motor off
  delayMicroseconds(speedpulse);     //delay

  //Notes count pulse. 400 pulses = 1 revolution
  //count time elapsed since start of counting and convert to seconds for rpm calculation
  //calculate feed speed at 0.100"/revolution
}