RPM Hall effect sensor code inside a While loop for a stepper motor.

Hi,

I am new to programming so please bear with me. This is my very first program. I have written some code to control the speed of a stepper motor.

  1. I have a momentary button that selects a mode 1 thru 4.
  2. Mode selection will set a variable for speed to the stepper motor.
  3. There is a switch button that will start or stop the motor once mode is selected.

Modes 1-4 run at a different speed based on which one is selected and everything works really well so far.

Now, I want to add a magnetic hall effect sensor to the mix and assign the RMP to a variable.

I have code that will do this as intended but integrating this code to my stepper motor code has proven problematic.

The stepper motor runs in a While Loop. It runs while steps do not equal an assigned amount of steps.

I added the RPM code inside the While loop of the stepper so it would record RPM while the motor is turning.

When i run the code and select a mode the motor turns very slow steps at a time. I am guessing it turns the motor 1 step pauses while running the added RPM code then repeats. so the stepper only turns a step then pause, step then pause for all modes selected.

How can I run the RPM code while the Stepper motor is turning at the same time?

void loop()
{
  modeState = digitalRead(modePin); // read the pushbutton input pin (high or low)and assign it to buttonState:
  buttonState = digitalRead(buttonPin); // read the pushbutton input pin (high or low)and assign it to buttonState:
  //Serial.print("Mode State: ");
  // Serial.println(modeState);
  //Serial.print("Last Mode State: ");
  //Serial.println(lastModeState);

// ---------------------------------------- increament the Mode counter 1-4-------------------------------------------
  if (buttonState == LOW && modeState != lastModeState)
  {
     
      if (modeState == HIGH)
      {
          // if the current state is HIGH then the button went from off to on:
          delay(300);
          modePushCounter++;
          Serial.print("Mode: ");
          Serial.println(modePushCounter);
          modeState = digitalRead(modePin); // read the pushbutton input pin (high or low)and assign it to buttonState:

      }

      else
      {
          Serial.println("Mode State No Increment");
      }
      delay(50);
      //  }

      // lastModeState = modeState; // save the current state as the last state, for next time through the loop
      if (modePushCounter > 4)
      {
          (modePushCounter = 1);
          Serial.print("Reset to: ");
          Serial.println(modePushCounter);
      }
      
      if (modePushCounter ==1)
      {
      rpmSpeed = 50;
      stepperSpeed= 150;
      }
      if (modePushCounter ==2)
      {
      rpmSpeed = 60;
      stepperSpeed= 300;
      }
      if (modePushCounter ==3)
      {
      rpmSpeed = 70;
      stepperSpeed= 700;
      }        
      if (modePushCounter ==4)
      {
      rpmSpeed = 80;
      stepperSpeed= 800;
      }
      
      // -----  Display Text  -------------
      u8g.firstPage();
      do {
          draw7();
      } while( u8g.nextPage() );
      delay(50);
      // -----------------------------------

  }
  // ---------------------------------------- increament the Mode counter 1-4-------------------------------------------

    if (buttonState == HIGH)
  {

          // -----  Display Text  -------------
          u8g.firstPage();
          do {
              draw1();
          } while( u8g.nextPage() );
          delay(50);
          // -----------------------------------

          Serial.print("modePushCounter: ");
          Serial.println(modePushCounter);
          Serial.println("Motor 50 RPM");
          Serial.println(stepperSpeed);

          stepper.setCurrentPosition(0); //set the current position to 0
          
          while(stepper.currentPosition() != -8000)
          {
              stepper.setSpeed(stepperSpeed);
              stepper.runSpeed();
              modeState = digitalRead(modePin);
              
              //delay(500);//crude form of button debouncing - 1000ms = 1s

              buttonState = digitalRead(buttonPin); // read the pushbutton input pin (high or low)and assign it to buttonState:
              if (buttonState == LOW )
              {
                  stepper.setCurrentPosition(-8000);   //This stops the motor from looping until i can get the brake command working.
                  //break;
                  Serial.print("Pump Stoped ");

                  // -----  Display Text  -------------
                  u8g.firstPage();
                  do {
                      draw6();
                  } while( u8g.nextPage() );
                  delay(50);
                  // -----------------------------------
                  // modePushCounter++;
              }
       // RPM CODE BEGINS BELOW 
                    // Preallocate values for tach
                          float hall_count = 1.0;
                          float start = micros();
                          bool on_state = false;
                         // counting number of times the hall sensor is tripped
                         // but without double counting during the same trip
                        
                      while(true)
                      {
                          if (digitalRead(hall_pin)==0){
                            if (on_state==false){
                              on_state = true;
                              hall_count+=1.0;
                              Serial.print(hall_count);
                            }
                          } else{
                            on_state = false;
                          }
                          
                          if (hall_count>=hall_thresh){
                            break;
                          }
                      }
              
                        // print information about Time and RPM
                        float end_time = micros();
                        float time_passed = ((end_time-start)/1000000.0);
                        Serial.print("Time Passed: ");
                        Serial.print(time_passed);
                        Serial.println("s");
                        float rpm_val = (hall_count/time_passed)*60.0;
                        Serial.print(rpm_val);
                        Serial.println(" RPM");
                        delay(1);        // delay in between reads for stability

     // RPM CODE ENDS
          }
  }

  // Delay a little bit to avoid bouncing
  
  delay(50); // 1000ms = 1s
}

Mode_Selection4.ino (10 KB)

You might want to rewrite the whole thing as a state machine. Events would be your buttons, switch and Hall effect sensor. Don’t use any delay() in the code or you’ll be stuck there and don’t have active waiting loops on just one event or the others won’t be checked

Thanks for your help, I guess it is not possible to do with arduino. I will look up what a state machine is.

Thanks anyway.

taylordw72:
Thanks for your help, I guess it is not possible to do with arduino. I will look up what a state machine is.

Thanks anyway.

It's entirely doable using an Arduino. But, as JML says, your delay() statements mess with your timing.

"State Machine" is a broad definition. Anything with a switch or button can be called a state machine.

But, in your code, you have a forever loop:

while (true)

This could be written as:

while (true){
  //Do everything here because nothing else in my code will ever execute
}

You have the right idea about counting pulses from the Hall sensor.

Keep you loop() code as tight as possible. That means no pause statements and no while statements that never end.

A state machine is a way to architect your code in “states”. It is doable with an arduino

In my void loop there is a While loop, It spins a stepper until it reachs 8000 steps. I guess my original question is can i have the RPM code in the same While loop to record RPM while the motor is spinning.

I tried this and the motor would barley turn regardless of mode selection. Are you saying remove all delays and this code should work?

Can we see your whole code and on what Arduino platform are you running?

A state machine architecture looks something like this:

void setup() 
{
    .
    .
    .
    
}//setup

void loop() 
{
    StateMachine_1();
    StateMachine_2();
    
}//loop

void StateMachine_1( void )
{
        
    switch( state1 )
    {
        case    DO_A:
            if( time to do A )
            {
                do A thing
            }
            
        break;

        case    DO_B:
            if( time to do B )
            {
                do B thing
            }
            
        break;
    }
}

void StateMachine_2( void )
{
        
    switch( state2 )
    {
        case    DO_C:
            if( time to do C )
            {
                do C thing
            }
            
        break;

        case    DO_D:
            if( time to do D )
            {
                do D thing
            }
            
        break;
    }
}

There are no delays(). Everything is timed or sequenced. You can use millis() or micros() to time things.

An example might be a switch read. There’s no reason to read a human-actuated switch every 10uS. Maybe something like this:

bool SwitchRead( void )
{
    static unsigned long
        timeSwitch = 0;
    unsigned long
        timeNow;

    timeNow = millis();
    if( timeNow - timeSwitch >= SWITCH_READ_INTERVAL )
    {
        timeSwitch = timeNow;
        read switch
        if( switch has been pressed )
            return true;
        else
            return false;
            
    }
    
}

You could call SwitchRead() from loop every pass but the logic only runs once every SWITCH_READ_INTERVAL mS (for example, 50000 or 50mS). In this type of architecture you sort of peek into things and see if they need doing and if they don’t, you get out immediately. If they do, you service them as quickly as you can. If you need time delays, structure your routines to do this with millis() (e.g.) instead of using delay().

For measuring RPM you might consider using a pin-change interrupt and have a separate function that looks for updates from the interrupt service routine and computes and updates the RPM variable.

taylordw72:
In my void loop there is a While loop, It spins a stepper until it reachs 8000 steps.

No, it doesn’t. It runs forever and ever. The break statement only exits a switch statement. It does nothing here.

Replace the while statement with this:
while (hall_count < hall_thresh)
{
if (digitalRead(hall_pin) == 0) {
if (on_state == false) {
on_state = true;
hall_count += 1.0;
Serial.print(hall_count);
}
} else {
on_state = false;
}
}
As to will it work? More likely since the while loop now has an end.
Delays are generally frowned upon When possible, get familiar with using the millis timer. (See “Using millis() for timing. A beginners guide” at the top of the forum).
Your problem with delays here is that you start the motor, print something and delay a few ms, then you start counting turns.
Also, you should avoid using absolutes in if or while statements:

while (stepper.currentPosition() != -8000)

Can you guarantee that the function will return exactly -8000 and not -8001 or -7999?

SteveMann:
The break statement only exits a switch statement.

really?

Accelstepper reference

Instead of blocking everything in the while() where you see if you have arrived with stepper.currentPosition(), why not use stepper.move() or stepper.moveTo() which are non-blocking. Then stepper.run or stepper.runSpeed (the former including accelerations, the latter for constant speed) will step the motor one step nearer to the target delaylessly each time they are called.

(YMMV: I'm no expert on steppers, haven't used them much, but that's what the reference says and what my recent experimenting playing around showed.)

PS to previous:

The AccelStepper code below uses .move() and .run() to move a stepper delayless-ly using acceleration, as proved by watching L13’s bwod. (I used .distanceToGo() to see if the stepper had arrived, and then reversed direction.)

So there’s no reason you shouldn’t be able to read the Hall sensor to determine RPM, in real time. I would test that for you, except I epoxied my only Hall sensor to a continuous servo a while back, for the same purpose, and it’s there for keeps :wink:

// accelsteper dir test
// 28 aug 2019
//   now with bwod to prove move and run don't block
//   18 sept 2019

#include <AccelStepper.h>
#define HALFSTEP 8
// Motor pin definitions
#define motorPin1  8     // IN1 on the ULN2003 driver 1
#define motorPin2  9     // IN2 on the ULN2003 driver 1
#define motorPin3  10     // IN3 on the ULN2003 driver 1
#define motorPin4  11     // IN4 on the ULN2003 driver 1

// Initialize with pin sequence IN1-IN3-IN2-IN4 for using the AccelStepper with 28BYJ-48
AccelStepper stepper1(HALFSTEP, motorPin1, motorPin3, motorPin2, motorPin4);

int dir = -1;
bool dirIsSet = false;
int numberOfSteps = 8192;

//bwod
unsigned long lastBlink;
int blinkInterval = 250;
bool blinkState = LOW;

void setup()
{
  Serial.begin(9600);
  Serial.println(".... accelsteper dir test with acceleration and bwod L13 ....");
  Serial.print("Compiler: ");
  Serial.print(__VERSION__);
  Serial.print(", Arduino IDE: ");
  Serial.println(ARDUINO);
  Serial.print("Created: ");
  Serial.print(__TIME__);
  Serial.print(", ");
  Serial.println(__DATE__);
  Serial.println(__FILE__);

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, blinkState);

  stepper1.setMaxSpeed(1000.0);
  stepper1.setAcceleration(100.0);

} //setup

void loop()
{

  bwod();

  if (!dirIsSet)
  {
    dir = -1 * dir;
    Serial.print("setting dir ");
    Serial.print(dir * numberOfSteps);
    stepper1.move(dir * numberOfSteps);
    dirIsSet = true;
  }

  if (stepper1.distanceToGo() == 0)
  {
    Serial.print("...done");
    delay(500);
    Serial.println("..reversing..");
    dirIsSet = false;
  }

  stepper1.run();
} //loop

void bwod()
{
  if (millis() - lastBlink >= blinkInterval)
  {
    lastBlink = millis();
    blinkState = !blinkState;
    digitalWrite(LED_BUILTIN, blinkState);
  }
}

taylordw72:
The stepper motor runs in a While Loop.

As others have said, if you want a responsive program don't use WHILE and don't use delay(). Allow loop() to do the repetition as it it is designed for and use millis() for non-blocking timing.

The demo Several Things at a Time illustrates the use of millis() to manage timing without blocking. It may help with understanding the technique. Note how each function runs very briefly and returns to loop() so the next one can be called. None of the functions tries to complete a task in one call. And there may be dozens of calls to a function before it is actually time for it to do anything.

Have a look at Using millis() for timing. A beginners guide if you need more explanation.

The second example in this Simple Stepper Code uses non-blocking timing. Or use the AccelStepper library as already suggested.

...R

Robin2:
Or use the AccelStepper library as already suggested.

OP is using it; just not well :wink:

Wow, Thanks to everyone for your time, examples and knowledge. I tried to upload the complete program but the forum limits the lines of code that can be posted. This is why I also attached a copy of the complete code in the original post.

There is a lot of information provided above and will take me a bit to digest, implement and test. I opened my first C programming book 4 weeks ago, your guidance in pointing me in certain directions helps tremendously.

Ill keep you posted.