Stepper motor hogs cycles

Hi,

I'm trying to run a stepper motor as a second-hand for a chronometer. A single button will start, stop and reset the second hand in that order.

I need it to revolve at 1rpm, obviously!

I find that once the motor is running the sketch will not accept any further input until the motor has completed one full revolution.

I'm guessing that telling it to run for 2048 steps is not the way to do it, but i can't see any other way of guaranteeing it will reolve at the correct speed. helop appreciated:

/*
  State change detection (edge detection)

  Often, you don't need to know the state of a digital input all the time, but
  you just need to know when the input changes from one state to another.
  For example, you want to know when a button goes from OFF to ON. This is called
  state change detection, or edge detection.

  This example shows how to detect when a button or button changes from off to on
  and on to off.

  The circuit:
  - pushbutton attached to pin 2 from +5V
  - (10 kilohm resistor attached to pin 2 from ground)
  - acutally INPUT_PULLUP used for button

  created  27 Sep 2005
  modified 30 Aug 2011
  by Tom Igoe

  Modified badly by Roger October 2023

  This example code is in the public domain.

  https://www.arduino.cc/en/Tutorial/BuiltInExamples/StateChangeDetection
*/
#include <Stepper.h>
// this constant won't change:
const int buttonPin = 6;  // the pin that the pushbutton is attached to

// Variables will change:
int buttonPushCounter = 0;  // counter for the number of button presses
int buttonState = 0;        // current state of the button
int lastButtonState = 0;    // previous state of the button

const int stepsPerRevolution = 2048;

//Use pin 2-5 to IN1-IN4

Stepper CAPT_chrono_needle = Stepper(stepsPerRevolution, 5, 3, 4, 2);

void setup() {
  //Set the RPM of the stepper motor
  CAPT_chrono_needle.setSpeed(1);
  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT_PULLUP);
  // initialize serial port
  Serial.begin(9600);
  Serial.println(buttonState);
}


void loop() {
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == LOW) {
      // if the current state is HIGH then the button went from off to on:
      buttonPushCounter++;
      if (buttonPushCounter > 2) { buttonPushCounter = 0; }
      switch (buttonPushCounter) {
        case 0:
          //do something when var equals 0
          Serial.println("Chrono home");
          break;
        case 1:
          //do something when var equals 1
          Serial.println("Chrono running");
           CAPT_chrono_needle.setSpeed(1);
          CAPT_chrono_needle.step(stepsPerRevolution);
          break;
        case 2:
          //do something when var equals 2
          Serial.println("Chrono stopped");
           CAPT_chrono_needle.setSpeed(0);
          break;
        default:
          // if nothing else matches, do the default
          // default is optional
          break;
      }
      //Serial.println("on");
      Serial.print("number of button pushes: ");
      Serial.println(buttonPushCounter);
    } else {
      // if the current state is LOW then the button went from on to off:
      //Serial.println("off");
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state, for next time through the loop
  lastButtonState = buttonState;


}

The stepper-library is the most unsuitable one for such tasks.
accelStepper might work. But I recommend using a different stepper-motor-library
The name is MobaTools.
The MobaTools create the step-pulse "in the backround" this enables to execute code "in parallel" to the step-pulse-creation.

This means you can read in buttons all the time and stop the stepper-monitor at any time.

I do not understand what you mean by "second hand"
I'm pretty sure it is not meant as "buy a used product second hand"

So does this mean

  • first button-press makes the stepper-motor run
  • second press stops the stepper-motor
  • third press moves the stepper-motor and the pin back to zero?

should be pretty easy to do with the MobaTools

best regards Stefan

1 Like

Lots of more elegant approaches will be suggested, but couldn't you run 60 x 2048/60 steps? (yes, I know, non-integer result, read on).
Resolves to 34.1333 steps per second. However, there's going to be some software overhead in there, might actually offset that 0.1333.
I'd try that, anyway.

How accurate does that one minute need to be?

1 Like

Hi guys,

Thanks for all the w responses. I’m afk right bow but will get to these this evening.

The minute has to be pretty accurate. The timer could run for half an hour.

I have an optical sensor which I can introduce for calibration - but I sense that may be another can of worms!

This answer does not specify numbers.

What deviation from exact 30,0000 minutes = 1800,000 seconds = 1.800.000 milliseconds is acceptable as the maximum deviation?

The solution will depend on that deviation.

1 Like

Here is a MobaTools program to run 1 turn at 1 RPM. Is this accurate enough? Note that you can adjust the speed in 1/10 RPM increments to help to achieve the longer term accuracy that you want.

The MobaTools library is available for installation via the IDE library manager.

#include <MobaTools.h>

int spr = 4096;  // steps per revolution
MoToStepper Step1(spr);           // HALFSTEP is default

void setup()
{
   Step1.attach( 4, 5, 6, 7 );

   Step1.setSpeed( 10 ); // = 1 RPM
   Step1.setRampLen(1);
   Step1.setZero();

   Step1.move(spr); // 1 revolution
}

void loop()
{
}
1 Like

Something this simple might work if you could weave it into your pgm. :grin:

#include <Stepper.h>

const int stepsPerRevolution = 2048;
unsigned long timer,
              elapsed, 
              interval = 29296; // adjust in steps of 4 for calibration
 //Use pin 2-5 to IN1-IN4

Stepper CAPT_chrono_needle = Stepper(stepsPerRevolution, 5, 3, 4, 2);

void setup() {
  //Set the RPM of the stepper motor
  CAPT_chrono_needle.setSpeed(1);
  
  //Serial.begin(9600);
}

void loop() {
  elapsed = millis() - timer;
  
  if(elapsed >= interval) {
    timer += interval;  
    CAPT_chrono_needle.step(1); // or (-1)
  }  
}

About 2070 bytes

1 Like

Thanks again guys.

I guess maybe 30 seconds inaccuracy would be acceptable. The chronometer is planned to go into my flight simulator cockpit. Even when playing, one can end up in “the hold” for a while. Each "lap" round the hold takes about 4 minutes. If you're in the hold for 30 mins, then thats 7.5 laps. Maybe 3 seconds per lap, so 25 - 30 seconds over 30 mins.

So an accuracy of 1 second/minute would be optimal, i.e. 1,000/1,800,000 milliseconds.

WHOOPS That should have been:

elapsed = micros() - timer;

Remember, I said MIGHT.

1 Like

I estimate the precision will be higher than 2 seconds per minute.
It depends on the timer-interrupt-frequency of the mobatools.

What exact type of microcontroller are you using?

@MicroBahner :
What is the timer-interrupt-frequency of the mobatools?

best regards Stefan

1 Like

I'm using an Arduino MEGA 2560 with a 28BYJ-48 stepper motor driven by a ULN2003 driver board. I'm not sure this board is compatible with MobaTools though.

It is.

1 Like

See the demo-code of groundfungus in post # 7

1 Like

Hi Guys,

Been playing with MobaTools this eve - seems it might just work! Thanks for the guidance. I have a few hours of learning ahead of me so I'll be quiet for a while - but no doubt will have more questions later. Thanks again for the help - so quickly too! I haven't marked any post as the solution yet, since you all contributed. Not sure how to do that - unless i can mark more than one post as the solution?

Just a "like" is enough for my pithy contribution. :wink: Good luck.

1 Like

Good morning guys.

I played with it till well past midnight last night.

I now have it starting and stopping with the button. I just need to work out how to tell the needle to go back to zero (at a faster speed) when the button is pressed for the third time. In reality, the needle will always take the shortest path back to the zero position, so I'm guessing I'll need to see how many steps it has completed when I do press #3 - and then give it a negative value if less than half, or positive if half or more.

I'll let you know how I get on and post code once working.

many thanks!

If using MobaTools and using the setZero() function in setup() you should be able to call a moveTo(0) function and the motor will go to the starting position. The library keeps track of position so knows where home is.

The move function is relative. If you say move(100) it will move 100 steps. If you say move(100) again it will move another 100 steps.

The moveTo() function is absolute. If you are at 0 and say moveTo(100) the motor will move 100 steps. If you say moveTo(100) again nothing will happen since it is already at 100.

1 Like

If you execute setZero each time the pin reaches 0 minutes again
a moveTo(0) does only one rotation

for a higher speed use the command setSpeedSteps()

best regards Stefan

1 Like

So much help guys! I'm very grateful for your time. I'm afk most of the day but I'll be tearing into it this evening!