RPM measurement of stepper motor

Any variable to do with millis needs to be unsigned long, not int. I expect that's partly responsible for your negative values.

This is highly likely to be zero, which I doubt is what you were looking for:

   tsrpm = millis() - millis();

This is odd too:

  rpm = 60 / ((tfrpm - tsrpm) / 1000);

No mention of the number of pulses.

with the millis - millis, if I understand correctly,the millis could be very long (counting since the last reset?) so I thought that for the start of the count, to get zero, I would subtract them and get zero, then the next milli count would be just the difference. I was thinking that milli would be too long for a variable, eventually, regardless of which format I use. But it sounds like that thinking on my part is incorrect.

And yes, the rpm does not account for pulses. I got so wrapped up in time, forgot.

Thanks. Will post back.

…and once you have RPM, you can do one more simple calculation to display your feed rate in whatever unit is convenient. mm/sec etc

Forgive me please. Maybe I am missing the obvious. I think I left the pulses off because I am taking the time to complete one revolution. tfrpm (time at the final pulse count) - tsrpm (time at the start of the pulse count, ie 400 pulses earlier) The pulses is only for the counter, is it not? I should be able to take the time for one revolution (in milliseconds) and work that with 60 seconds to get RPM, no? Or do I need to work pulses into the rpm calculation? So, say 500 milliseconds to do the 400 loops, then the rpm should be 60/(500/1000) (to convert milliseconds to a decimal to work with the 60?
Having said that, I still cannot get the rpm to display correctly.

this piece of code results in the rpm being displayed as 03 always. rpm is shown as unsigned long but I also had it as int. That did not work either. Anyone see anything? It will be a newbie mistake... :thinking:

int limit = 0;              //limit switch state
unsigned long tsrpm = 0;               //tsrpm is start of timer for rpm
unsigned long tfrpm = 0;               //tfrpm is stoptime for rpm count
int pulses = 0;                //pulse is counter for loop cycles to use for rpm
unsigned long 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();
  }
   if (pulses == setpulse)
  { tfrpm = millis();
    pulses = 0;
  }
  
  rpm = 60000/( tfrpm - tsrpm);

That helped wildbill. I decided to just display when the pulse count was at zero so once per revolution. I made a few changes at the same time and it seems to be working much better now!

The motor still seems noisy which I suspect is the delays. I am going to explore the accelstepper library and also interrupts more.

But thanks everyone for the input. It all helped.
Cheers,
Shawn

#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
unsigned long tsrpm = 0;               //t1rpm is start of timer for rpm
unsigned long 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() {

if (pulses == 0) {
  lcd.clear();
  rpm = 60000 /(tfrpm - tsrpm);
  lcd.print("RPM = ");
  lcd.print(rpm);
  lcd.setCursor(0,1);
  lcd.print("in/min = ");
  lcd.print(rpm*0.1);
  
}
  pulses++;

  if (pulses == 1)  {
    tsrpm = millis();
  }
  else if (pulses == setpulse){
   tfrpm = millis();
    pulses = 0;
  }

  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("-");
  //lcd.print(tsrpm);
  //lcd.print("-");
  //lcd.print(millis());
  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

}

That delay(5) is likely a problem, try commenting it out.

Yes, thanks. It was left over from troubleshooting when I needed to be able to read the display. It will be removed.

I noticed that the rpm is always whole numbers. I then see that an int variable will do that. I found an example of float that shows decimal places but when I tried it, I got an ovr? I think on the display..it lead me to think overflow. I tried changing the math a bit to work with smaller numbers but it did not seem to help. Is that where I should be ? Using float and a formula that does not cause an overflow? or am I on the wrong track? I would be good with just one decimal place but 2 would be good. The feed display shows 2 but it just multiplies the rpm by 0.1 cause of the 0.1" movement/revolution. So the display is ie 12 rpm, feed is 1.20. I think I will eventually drop the rpm display as it is the feed rate I want, but would like to have accurate first and second decimal places.

Too much current?

The first delayMicroseconds() should only delay as much as the stepper driver needs. It has nothing to do with the speed of the motor. The second delayMicroseconds() is the one causing the delay in your loop, which affects the speed, right?

If speedpulse is a very low value, who knows, maybe the pulse signal to the driver will be too short and it will lose a step now and then. Might add to the noise.

Run a wire from pulse pin to pin 2 (external interrupt) and use one of the numerous tachometer programs.
But that could only tell you if the drive was receiving pulses, only way to tell the motor is moving is an encoder or pulse generator physically attached to the driveline

In your approach you get one stepping pulse per loop cycle. As @Johan_Ha already pointed out, the first delayMicroseconds(speedpulse); should only be as long as the driver needs.
Your second delay should define the stepping rate. But that's not really true. Additionally to that delay, all statements in loop have to be executed until the next pulse is generated. And this time cannot be ignored. Even worse: it is not always the same with every cycle. So you don't get a smooth steprate, und your motor will be noisy.

As already suggested, you could try creating the step pulses by means of the micros() command - similar to 'blink witout delay. But even than you cannot reach smooth steprates faster than the longest cycletime of your loop(). This is also true when using AccelStepper, because the run() method of this library works very similar.

NB: It doesn't make sense to include the stepper.h if you don't use any calls of it.

Thank you all! I have it working, ok now, but I will continue to try to incorporate the suggestions here. It sounds like there are many things I could do to improve it, as I get more familiar with each command/function. I am going to shift to the physical motor mount now, and then I could try/test the prototype control circuit to see how it works in the mill.

Shawn

There should be no need whatever to use a float!

Ok, I will look at some tach programs to see how they do it. It would be nice to be able to show the feed rate with a little more resolution, but, realistically, as I am milling, it will be an "observe and adjust" operation so not really necessary. However, the lesson learned would be good. I recall from school (4 decades ago) there was something about working with larger numbers, then shifting the decimal point for display purposes to show the actual resolution. I would think that there is a better way to do it with Arduino (and evolution.. :slightly_smiling_face:) but have not gotten that far yet. Lots to learn

The motor is 2.8 Amp and the controller switches are set to 2.8 (2.9 peak) Would that be the correct setting or do users usually go down one setting? or up?

Interesting...I tried different length delay for the on pulse last night and did not see a large difference. Looking at the controller specs, it says the max frequency is 20K at 25 %duty cycle, and only 13K at 50%. So if my reasoning is correct, it needs a minimum of 50 uS pulse? I did use 100 at one point last night but I think I will set it to 100 today and let the control pot set the off delay accordingly. For some reason (new to this), I assumed it should be 50% but now that you and others have brought it up, just an on pulse makes sense.

Here is the data sheet for the controller

I will keep your comments in mind as I experiment.

Again, Thanks all.

Thinking about the on pulse duration, my max rpm will be about 100 and that will be crazy fast. So, please correct me if I am wrong, 100 rpm=1.67 rev per second. At the setting of 400, that would be ~42 milliseconds per pulse (on and off) so well within the controller specs, correct? So even a 1 milli second on pulse should be more than enough, correct? Good to know though because when I first started doing this program, the higher pulse rates were too slow, albeit smoother so now knowing what I know, and the programming a little more efficient, I should be able to use higher pulse rates, yes?

I will try today.

With 1.67 rev/sec and 400 steps/rev you need 668 steps/sec. Aren't that 1.5ms per step and not 42ms ? That's a big difference ...

Oops. Yes. Thanks

I have been trying to incorporate an interrupt for the limit switch violation (taking the check out of the main loop) and trying to incorporate functions instead of one big loop. I have been trying to track down the errors all evening but can't seem to make headway. Can anyone give me some guidance please?

I tried have one loop for spinning the motor, and then another loop to display data on the LCD, but only once per loop. The limit or "stopp" function deals with the limit switch getting set and I was hoping to be able to flag it, then reset it by bumping the jog button. I would not want the motor to start right away when I cleared the table away from the limit so hoping to require a reset of some fashion, without adding another button. I know that part of the code needs work but I can't get past the numerous errors.

Thanks in advance.

#include <LiquidCrystal.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!)
   6600 motor controller set to 400
*/

const int dirpinout = A5;             //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
const int enablepin = A6;             //enable pin out to controller
int enableout = 1;          //sets motor off to start
const int pulse = A4;              // pulse out pin to controller
const int potin = A0;       //speed pot connected to pin A0
int speedpulse;         // pulse delay for speed management
const int runpin = A1;             //run button enables or disables motor
int runn = 1;             //sets runn to state of runpin (low to run)
const int jogin = A3;             //jog switch in
int jogout = 0;           //jog out
const int limitpin = 2;     //limit switch input
const int limitled = 13;    // limit indicator connected to pin 13** may not be connected
int limit = 1;            //limit switch state
unsigned long tsrpm = 0;    //tsrpm is start of timer for rpm
unsigned long tfrpm = 0;     //tfrpm is stoptime for rpm count
int loops = 0;                //loops is counter for loop cycles to use for rpm
int rpm = 0;                 //rpm
const int setpulse = 800;    //set to pulse setting on controller
volatile int stopped = 0; //stop is used to stop motor till a manual reset by activating jog

// 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 limitisr () {
  int stopped = 0;

}


void setup() {

  pinMode(limitpin, INPUT_PULLUP);     //limitswitch low when violated
  pinMode (dir_in, INPUT_PULLUP);     // switch takes pin low, otherwise, pin is high due to pullup
  pinMode(runpin, INPUT_PULLUP);    //Run button enables motor to run
  pinMode(jogin, INPUT_PULLUP);     //Jog button in

  pinMode (limitled, LED_BUILTIN);   //just an indicator
  pinMode (enablepin, OUTPUT);    //high to controller to enable
  pinMode (pulse, OUTPUT);       //pulse to controller
  pinMode(dirpinout, OUTPUT);    //dir to controller for clockwise ****check this

  lcd.begin(16, 2);   // set up the LCD's number of columns and rows:
  loops = 0; //sets completed loops to 0

  attachInterrupt(digitalPinToInterrupt(limitpin), limitisr, LOW);
}

void loop() {       //main loop

  // displaydata();      //goes to display data function
  //spinmotor();      //spin the motor

}

void spinmotor() {
  digitalWrite(limitled, !stopped);      //lights indicator***************************
  if (stopped == 0)   //checks to see if limit flag has been reset
    stopp();  //if not, go to stopp function
  }

 ++loops;              //increments counter for loops
if (loops == 1)  {    //gets time of first loop since a reset
  tsrpm = millis();    //sets the time of the start tsrpm
  else if (loops == setpulse) {    //checks to see if the number of loops equals the number of steps on controller
    tfrpm = millis();      //gets final time for rpm calculation
    loops = 0;             //resets loops counter

  }
}
limit = digitalRead(limitpin);      //one to run, zero if at limit
runn = digitalRead(runpin);         //zero to run, one to not run
jogout = digitalRead(jogin);
dir = digitalRead(dir_in);       //reads motor direction according to switch
digitalWrite(dirpinout, dir);   //sets dir pin to controller

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

}

void spinmotor() { // reads speed control and spins the motor

  speedpulse = analogRead (potin);         //reads speed pot
  speedpulse = map (speedpulse, 0, 1023, 250, 16250); //converts pot to speed delays max is 16383
  if (stopped = 1) {    //if stopped flag is set
    stopp();     //  then goto stopp function, otherwise, do the following
  }
  digitalWrite (pulse, HIGH);           //pulses motor
  delayMicroseconds(100);      //100 usec pulse delay
  digitalWrite (pulse, LOW);          //pulses motor off
  delayMicroseconds(speedpulse);     //delay
  delayMicroseconds(speedpulse);     //delay

}

void displaydat() { //function to display data is called once per loop

  lcd.clear();
  rpm = 60000 / (tfrpm - tsrpm);  //converting numbers to minute values
  lcd.print("RPM     Feed in/min");
  lcd.setCursor(0, 1);
  lcd.print(rpm, 1);
  lcd.print("  ---  ");
  lcd.print(rpm * 0.1);
}

void stopp()  {  //stopp function for limit violation
  lcd.clear(); //writes situation to display
  delay(500);
  lcd.print("Limit reached");
  lcd.setCursor(0, 1);
  lcd.print("Push Jog to reset");
  delay (1000);
  if digitalRead(A3) = 0{ //if jog button pushed, reset stopp flag, else, don't
    stopped = 1;
  }

Here is a quick video of the hardware and arduino working, if interested. I am now trying to incorporate some of the suggested changes. I have not gotten to the rpm stuff yet.