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.
I have a momentary button that selects a mode 1 thru 4.
Mode selection will set a variable for speed to the stepper motor.
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
}
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
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:
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?
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.)
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
// 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.
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.