Using interrupts for stepper motor

Once the motorHome function is performed the serialEvent seems to freeze.
Any advise would be helpful - I would like to stay with stepper,h if posible.

/*
   Hardware:
  Arduino Nano (3)
  28BYJ-48 stepper motor (32 steps = one full rotation).
  ULN2003 driver board

   Wiring:
  Pin 8 to IN1 on the ULN2003 driver
  Pin 9 to IN2 on the ULN2003 driver
  Pin 10 to IN3 on the ULN2003 driver
  Pin 11 to IN4 on the ULN2003 driver

  Pin 2 to limit switch (NC)
  (gnd to gnd)


  23.Jan.2024
*/

// Include
#include "Stepper.h"


//Stepper motor - note the pin order: (8,10,9,11)
const int stepsPerRevolution = 2048;
const int rpm = 12;
const int iniPin[] = {8, 9, 10, 11};//0, 1,  2,  3
Stepper myStepper = Stepper(stepsPerRevolution, iniPin[0], iniPin[2], iniPin[1], iniPin[3]);


// sample timing
unsigned long previousMillis = 0;
const long interval = 1000;

// stop positions
const int motorPosition[] {0, 250, 560, 1000};

// limit Switch
const int limitSwitchPin = 2;

void setup() {

  Serial.begin(9600);

  myStepper.setSpeed(rpm);

  pinMode(limitSwitchPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(limitSwitchPin), motorHome, HIGH);
  interrupts ();
}

void loop() {

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    serialEvent();//<-----------------
  }
}

void serialEvent() {
  // get data only when serial is available
  if (Serial.available() > 0) {

    // read the incoming :
    int i = Serial.parseInt();

    // we received
    Serial.print("received: ");    Serial.println(i);

    switch (i) {
      case 1:
        Serial.print("1.. stop ");
        myStepper.step(0);
        break;
      case 2:
        Serial.print("2.. position 1 ");
        motorStopPosition(motorPosition[1]);
        break;
      case 3:
        Serial.print("3.. position 2 ");
        motorStopPosition(motorPosition[2]);
        break;
      case 4:
        Serial.print("4.. position 3 ");
        motorStopPosition(motorPosition[3]);
        break;
      case 5:
        Serial.print("5.. position HOME ");
        motorHome();
        break;
      case 6:
        Serial.print("6.. testing ");
        motorTest();
        break;
      default:
        //Serial.print("Unrecognized command!");
        break;

    }// end switch

    // cleanup...
    Serial.println(" ");

  }// end if
}// end serialEvent

//FUNCTIONS------------------------------------------------//
void motorHome() {
  int  limitSwitchState = digitalRead(limitSwitchPin);
  delay(500);
  Serial.print ("limitSwitchState  ");
  delay(500);

  while (limitSwitchState == LOW) {
    myStepper.step(stepsPerRevolution);
  }
}

void  motorStopPosition(int pos) {
  myStepper.step(pos);
}

void motorTest() {
  Serial.print(" testing  rpm "); Serial.println(rpm);
  myStepper.step(stepsPerRevolution);
  delay(500);
  myStepper.step(-stepsPerRevolution);
  delay(500);
}

That is not a function that you can run in an interrupt context. Interrupt functions have to be fast and can't include other code that needs interrupts to function like delay or Serial.

Your loop function is already non-blocking. Let the interrupt handler just stop the motor and set a flag and then let the loop function call the motorHome function in response to that flag.

There is a much easier approach than using stepper.h

The MobaTools-Library.
The reason is: the step-pulses are created in the background.
You write a few lines of code
initialisation

    myStepper.attach( stepPin, dirPin );
    myStepper.setSpeed( 1200 );  // 120 Umdrehungen/Min
    myStepper.setRampLen( 20 );

make stepper move

myStepper.doSteps(400);

while the mobatools create all the step-pulses in the backround based on a timer-interrupt your code can "go fishing" or whatever other thing you like

best regards Stefan

@dolittle
delay() will not work inside an interrupt function.
You need to make your interrupt routine much simpler.

Here is your code modified to use the MobaTools-library.
The interrupt is no longer needed.
Your stepper.h function-calls are not deleted just commented out
As you can see the code is very similar. A part of the function-calls is similar


/*
   Hardware:
  Arduino Nano (3)
  28BYJ-48 stepper motor (32 steps = one full rotation).
  ULN2003 driver board

   Wiring:
  Pin 8 to IN1 on the ULN2003 driver
  Pin 9 to IN2 on the ULN2003 driver
  Pin 10 to IN3 on the ULN2003 driver
  Pin 11 to IN4 on the ULN2003 driver

  Pin 2 to limit switch (NC)
  (gnd to gnd)

  23.Jan.2024
*/

//#include "Stepper.h"
#include <MobaTools.h>

const int stepRev = 2048;    // steps per revolution ( 1/16 microsteps )
MoToStepper myStepper( stepRev, FULLSTEP );// create stepper object

//Stepper motor - note the pin order: (8,10,9,11)
const int stepsPerRevolution = 2048;
const int rpm = 12;
const int iniPin[] = {8, 9, 10, 11};//0, 1,  2,  3
//Stepper myStepper = Stepper(stepsPerRevolution, iniPin[0], iniPin[2], iniPin[1], iniPin[3]);
// see function-call myStepper.attach in setup
// sample timing
unsigned long previousMillis = 0;
const long interval = 1000;

// stop positions
const int motorPosition[] {0, 250, 560, 1000};

// limit Switch
const int limitSwitchPin = 2;

void setup() {
  myStepper.attach( iniPin[0], iniPin[2], iniPin[1], iniPin[3] );

  Serial.begin(9600);

  myStepper.setSpeed(rpm * 10);  // the mobatools expect rpm10

  pinMode(limitSwitchPin, INPUT_PULLUP);
  //attachInterrupt(digitalPinToInterrupt(limitSwitchPin), motorHome, HIGH);
  //interrupts ();
}

void loop() {

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    serialEvent();//<-----------------
  }
}

void serialEvent() {
  // get data only when serial is available
  if (Serial.available() > 0) {

    // read the incoming :
    int i = Serial.parseInt();

    // we received
    Serial.print("received: ");
    Serial.println(i);

    switch (i) {
      case 1:
        Serial.print("1.. stop ");
        //myStepper.step(0);
        myStepper.stop();
        break;

      case 2:
        Serial.print("2.. position 1 ");
        motorStopPosition(motorPosition[1]);
        break;

      case 3:
        Serial.print("3.. position 2 ");
        motorStopPosition(motorPosition[2]);
        break;

      case 4:
        Serial.print("4.. position 3 ");
        motorStopPosition(motorPosition[3]);
        break;

      case 5:
        Serial.print("5.. position HOME ");
        motorHome();
        break;

      case 6:
        Serial.print("6.. testing ");
        motorTest();
        break;

      default:
        //Serial.print("Unrecognized command!");
        break;

    }// end switch

    // cleanup...
    Serial.println(" ");

  }// end if
}// end serialEvent

//FUNCTIONS------------------------------------------------//
void motorHome() {
  int  limitSwitchState;
  delay(500);
  Serial.print ("limitSwitchState  ");
  delay(500);
  
  myStepper.rotate(1); // you might change to myStepper.rotate(-1); to rotate the opposite direction
  while (limitSwitchState == LOW) {
    limitSwitchState = digitalRead(limitSwitchPin);
  }
  myStepper.stop();
}


void  motorStopPosition(int pos) {
  //myStepper.step(pos);
  myStepper.writeSteps(pos);
}


void motorTest() {
  Serial.print(" testing  rpm "); Serial.println(rpm);
  //myStepper.step(stepsPerRevolution);
  myStepper.rotate(1);
  delay(500);
  //myStepper.step(-stepsPerRevolution);
  delay(500);
  myStepper.rotate(-1);
}

Thanks for the info I have tried your suggestions and nothing seems to work - perhaps you would be kind enough to look at the code I used.

/*
   Hardware:
  Arduino Nano (3)
  28BYJ-48 stepper motor (32 steps = one full rotation).
  ULN2003 driver board

   Wiring:
  Pin 8 to IN1 on the ULN2003 driver
  Pin 9 to IN2 on the ULN2003 driver
  Pin 10 to IN3 on the ULN2003 driver
  Pin 11 to IN4 on the ULN2003 driver

  Pin 2 to limit switch (NC)
  (gnd to gnd)

  24.Jan.2024
*/


#include <MobaTools.h>

const int stepRev = 2048;    // steps per revolution ( 1/16 microsteps )
MoToStepper myStepper( stepRev, FULLSTEP );// create stepper object

//Stepper motor - note the pin order: (8,10,9,11)
const int rpm = 12;
const int iniPin[] = {8, 9, 10, 11};//0, 1,  2,  3

// sample timing
unsigned long previousMillis = 0;
const long interval = 1000;

// stop positions
const int motorPosition[] {0, 250, 560, 1000};

// limit Switch
const int limitSwitchPin = 2;

void setup() {
  myStepper.attach( iniPin[0], iniPin[2], iniPin[1], iniPin[3] );

  Serial.begin(9600);

  myStepper.setSpeed(rpm * 10);  // the mobatools expect rpm10

  pinMode(limitSwitchPin, INPUT_PULLUP);

}

void loop() {

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    serialEvent();//<-----------------
  }
}

void serialEvent() {
  // get data only when serial is available
  if (Serial.available() > 0) {

    // read the incoming :
    int i = Serial.parseInt();

    // we received
    Serial.print("received: ");
    Serial.println(i);

    switch (i) {
      case 1:
        Serial.print("1.. stop ");
        myStepper.stop();
        break;

      case 2:
        Serial.print("2.. position 1 ");
        motorStopPosition(motorPosition[1]);
        break;

      case 3:
        Serial.print("3.. position 2 ");
        motorStopPosition(motorPosition[2]);
        break;

      case 4:
        Serial.print("4.. position 3 ");
        motorStopPosition(motorPosition[3]);
        break;

      case 5:
        Serial.print("5.. position HOME ");
        motorHome();
        break;

      case 6:
        Serial.print("6.. testing ");
        motorTest();
        break;

      default:
        //Serial.print("Unrecognized command!");
        break;

    }// end switch

    // cleanup...
    Serial.println(" ");

  }// end if
}// end serialEvent

//FUNCTIONS------------------------------------------------//
void motorHome() {
  int  limitSwitchState = digitalRead(limitSwitchPin);
  Serial.print ("limitSwitchState  ");
  delay(500);

  myStepper.rotate(1); // you might change to myStepper.rotate(-1); to rotate the opposite direction
  while (limitSwitchState == LOW) {
    limitSwitchState = digitalRead(limitSwitchPin);
  }
  myStepper.stop();
}


void  motorStopPosition(int pos) {
  myStepper.writeSteps(pos);
}


void motorTest() {
  Serial.print(" test  rpm "); Serial.println(rpm);
  myStepper.rotate(1);
  delay(500);
  myStepper.rotate(-1);
}

is a way too unprecise description of what you observe.

  • does the code compile?

  • did you install the MobaTools with the library-manager of the Arduino-IDE?

what do you see printed in the serial monitor?

  • nothing?
  • if you see something printed to the serial monitor post it 1 to 1 as a code-section

what does your stepper-motor do?

  • really absolutely nothing?
  • humming or any kind of noise but does not turn?

did you analyse in detail which pin must be connected to what?

is this the "standard" 28BY-48 stepper-motor with 1:64 gear ?
or is it - which would be unusal - a pure 28BY-48 stepper-motor without gear ?

Your code does receive over serial interface.
This add another possability of possible bugs
does the motor turn if you use a code that does a basic test where moving back and forth is hardcoded?

Did you try different speeds?
Does the ULN2003 driver have an enable-pin that must be connected switched to level HIGH or Level LOW?

Here is a demo-code that simply rotates 180 degrees clockwise "CW" / counterclockwise "CCW"

This code contains quite some additional lines that serve for comfortable debugging through printing to the serial monitor

  • actual running code-version with timestamp of point in time when the code was compiled
  • by printing values of variables
  • blinking the onboard-LED as a visual feedback "code is running"
// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *
// a detailed explanation how these macros work is given in this tutorial
// https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298

#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);

#define dbgi(myFixedText, variableName,timeInterval) \
  { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  }

#define dbgc(myFixedText, variableName) \
  { \
    static long lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }

#define dbgcf(myFixedText, variableName) \
  { \
    static float lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *

unsigned long MyTestTimer = 0;                   // Timer-variables MUST be of type unsigned long
//const byte    OnBoard_LED = 2;     // onboard-LEDESP32 / ESP8266
//const byte    OnBoard_LED = 25;  // onboard-LED Raspberry Pi pico
const byte    OnBoard_LED = 13;   // onboard-LED uno, mega

/*
  if ( TimePeriodIsOver(MyTestTimer,1000) ) {

  }

*/

#include <MobaTools.h>

const int stepsPerRevolution = 2048;  // change this to fit the number of steps per revolution
//const int rolePerMinute = 15;         // Adjustable range of 28BYJ-48 stepper is 0~17 rpm
const byte stPn[] = {8, 9, 10, 11};//{2,3,4,5};

// initialize the stepper library on pins 8 through 11:
MoToStepper myStepper(stepsPerRevolution, FULLSTEP);

void setup() {
  Serial.begin(9600);   // initialize the serial port:
  Serial.println("Setup-Start");

  PrintFileNameDateTime();
  dbg("01", stepsPerRevolution);
  myStepper.attach(stPn[0], stPn[1], stPn[2], stPn[3]);
  myStepper.setSpeed(120);      // 240 (=24 rev/min) is too fast for this stepper!
  myStepper.setRampLen( 500 );
}


void loop() {

  Serial.println("clockwise");
  myStepper.write(180);          // go to 180° from starting position
  while ( myStepper.moving() ) { // // wait until stepper has reached 180°
    BlinkHeartBeatLED(OnBoard_LED, 250);

    dbgi("+180 CW", myStepper.stepsToDo(), 500);
    dbgi("+180 CW", myStepper.read(), 500);
    dbgi("+180 CW", myStepper.currentPosition(), 500);
  }
  delay(1000);

  // step one revolution in the other direction:
  Serial.println("counterclockwise");
  myStepper.write(-180);         // go to -180° from starting position
  while ( myStepper.moving() ) { // // wait until stepper has reached 180°
    BlinkHeartBeatLED(OnBoard_LED, 250);
    
    dbgi("-180 CCW", myStepper.stepsToDo(), 500);
    dbgi("-180 CCW", myStepper.read(), 500);
    dbgi("-180 CCW", myStepper.currentPosition(), 500);
  }
  delay(3000);
}

// helper-functions
void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}



void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}

and here is a WOKWI-Simulation where the code works non-blocking based on a state-machine

click the triangle to make the code-section visible
// https://forum.arduino.cc/t/using-interrupts-for-stepper-motor/1215423/8
// https://wokwi.com/projects/387863485345413121
// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *
// a detailed explanation how these macros work is given in this tutorial
// https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298

#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);

#define dbgi(myFixedText, variableName,timeInterval) \
  { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  }

#define dbgc(myFixedText, variableName) \
  { \
    static long lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }

#define dbgcf(myFixedText, variableName) \
  { \
    static float lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *

unsigned long MyTestTimer = 0;                   // Timer-variables MUST be of type unsigned long
//const byte    OnBoard_LED = 2;     // onboard-LEDESP32 / ESP8266
//const byte    OnBoard_LED = 25;  // onboard-LED Raspberry Pi pico
const byte    OnBoard_LED = 13;   // onboard-LED uno, mega

/*
  if ( TimePeriodIsOver(MyTestTimer,1000) ) {

  }
*/

#include <MobaTools.h>

const int stepsPerRevolution = 2048;  // change this to fit the number of steps per revolution
//const int rolePerMinute = 15;         // Adjustable range of 28BYJ-48 stepper is 0~17 rpm
const byte stPn[] = {8, 9, 10, 11};//{2,3,4,5};

// initialize the stepper library on pins 8 through 11:
MoToStepper myStepper(stepsPerRevolution, FULLSTEP);

const byte sm_idling           = 0;
const byte sm_clockwise        = 1;
const byte sm_counterclockwise = 2;
const byte sm_pausing1         = 3;
const byte sm_pausing2         = 4;

byte myState;


void setup() {
  Serial.begin(9600);   // initialize the serial port:
  Serial.println("Setup-Start");

  PrintFileNameDateTime();
  dbg("01", stepsPerRevolution);
  myStepper.attach(stPn[0], stPn[1], stPn[2], stPn[3]);
  myStepper.setSpeed(120);      // 240 (=24 rev/min) is too fast for this stepper!
  myStepper.setRampLen( 500 );

  myState = sm_pausing1;  // start with state sm_pausing1
  MyTestTimer = millis();
}

void loop() {
  BlinkHeartBeatLED(OnBoard_LED, 250);
  myStateMachine();
}


// state-machine initially              starts with state sm_pausing1

// when pausing1-time is over           go on with  state sm_clockwise
// when clockwise rotation has finished go on with  state sm_pausing2
// when pausing2-time is over           go on with  state sm_counterclockwise
// when CCW- rotation has finished      go on with  state sm_pausing1


void myStateMachine() {

  switch (myState) {
    case sm_idling:
      // do really nothing
      break; // IMMIDIATELY jump down to END-OF-SWITCH

    case sm_clockwise:
      if ( myStepper.moving() ) { // check if stepper-motor is still rotating
        dbgi("+180 CW", myStepper.stepsToDo(), 500);
        dbgi("+180 CW", myStepper.read(), 500);
        dbgi("+180 CW", myStepper.currentPosition(), 500);
      }
      else { // myStepper.moving() results in false
        Serial.println("pausing before counterclockwise");
        myStepper.write(-180);  // go to -180° from starting position
        MyTestTimer = millis(); // initialise timer-variable with actual time
        myState = sm_pausing2;
      }
      break; // IMMIDIATELY jump down to END-OF-SWITCH

    case sm_counterclockwise:
      if ( myStepper.moving() ) { // check if stepper-motor is still rotating
        dbgi("-180 CCW", myStepper.stepsToDo(), 500);
        dbgi("-180 CCW", myStepper.read(), 500);
        dbgi("-180 CCW", myStepper.currentPosition(), 500);
      }
      else { // myStepper.moving() results in false
        MyTestTimer = millis(); // initialise timer-variable with actual time
        myState = sm_pausing1;
      }
      break; // IMMIDIATELY jump down to END-OF-SWITCH

    case sm_pausing1:
      dbgi("pausing 1 before clockwise", 1, 1000);

      if ( TimePeriodIsOver(MyTestTimer, 4000) ) {
        Serial.println("start clockwise rotation");
        myStepper.write(180);          // go to 180° from starting position
        myState = sm_clockwise;
      }
      break; // IMMIDIATELY jump down to END-OF-SWITCH

    case sm_pausing2:
      dbgi("pausing 2 before counterclockwise", 1, 2000);

      if ( TimePeriodIsOver(MyTestTimer, 6000) ) {
        Serial.println("start counterclockwise rotation");
        myStepper.write(-180);          // go to 180° from starting position
        myState = sm_counterclockwise;
      }
      break; // IMMIDIATELY jump down to END-OF-SWITCH

  } // END-OF-SWITCH
}


// helper-functions
void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long & startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}



void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}

best regards Stefan

You are correct, and it was wrong of me to simply say 'it wont work" and I apologize for that.

I will spend some time to make sure that what I am trying to do works as expected and report my findings in more detail if they do not.

Thank you for pointing this out to me in a professional manner.

A quick question, if I am using both Analog and Digital how do I address this,

const byte btnPins[] = {A1, A2, A3, 2 }; // assign pins to the buttons

I am working my way thru MobaTools

Just like that. A1 is #define 'd somewhere to 15 in case of an UNO or whatever for the other boards. You can use A1 just like you would use the number 2 in that array.

Ok perhaps you could cast your eye on this code and advise me as what is wrong.
When using (doTest) the motor rotates cw and the ccw.
When using (doButtonTest)
pressing button A1 nothing happens with the motor or shows on serial
pressing button A2 nothing happens with the motor or shows on serial
pressing button A3 nothing happens with the motor but Home has been set.. on serial and the led shows up.

/*
   Hardware:
  Arduino Nano (3)
  28BYJ-48 stepper motor (32 steps = one full rotation).
  ULN2003 driver board

   Wiring:
  Pin 8 to IN1 on the ULN2003 driver
  Pin 9 to IN2 on the ULN2003 driver
  Pin 10 to IN3 on the ULN2003 driver
  Pin 11 to IN4 on the ULN2003 driver

  Pin 2 to limit switch (NC)
  (gnd to gnd)

  30.Jan.2024
*/


#include <MobaTools.h>

const int stepRev = 2048;    // steps per revolution ( 1/16 microsteps )-HALFSTEP and set the stepsPerRevolution to 4096
MoToStepper myStepper( stepRev, FULLSTEP );

//Stepper motor -
const int rpm = 12;
const byte iniPin[] = {8, 9, 10, 11};


// sample timing
unsigned long previousMillis = 0;
const long interval = 1000;

// stop positions
const int motorPosition[] {0, 250, 560, 1000};// just for testing

// buttons
const byte btnPins[] = {A1, A2, A3, 2 }; // assign pins to the buttons
const byte btnCnt = sizeof(btnPins);

// read analogue inputs and return states for MoToButtons
const int threshold = 500;
button_t readInputs() {
  button_t states = 0;
  for ( byte i = 0; i < 8; i++ ) {
    // read input und set according bit if value is greater than threshold
    if ( analogRead( btnPins[i]) > threshold ) states |= (1 << i);
  }
  return states;
}
MoToButtons myButtons( readInputs, 20, 500);
//MoToButtons myButtons( btnPins, btnCnt, 20, 500 );

// previous button state
byte prevbtn1 = -1;

// limit Switch
const int limitSwitchPin = 2;

// led
const int myLed1 = 3;
/**********************************************************************/
/**********************************************************************/

void setup() {
  Serial.begin(115200);

  myStepper.attach( iniPin[0], iniPin[1], iniPin[2], iniPin[3] );/******/
  myStepper.setSpeed(rpm * 10);  // the mobatools expect rpm10

  pinMode(btnPins[0], INPUT_PULLUP);// A1
  pinMode(btnPins[1], INPUT_PULLUP);// A2
  pinMode(btnPins[2], INPUT_PULLUP);// A3

  pinMode(limitSwitchPin, INPUT_PULLUP);// D2

  pinMode(myLed1, OUTPUT);  digitalWrite(myLed1, LOW);//D3
}/**********************************************************************/

void loop() {

  //doTest();//<----------------- this works just to check that everything is good


  myButtons.processButtons();     // comment these out if you are just testing
  doButtontest();//<----------------- will be the actual process

}/**********************************************************************/
void doTest() {// perform one rotation in each direction

  int  currentState1 = digitalRead(btnPins[0]);

  if (currentState1 != prevbtn1) {
    prevbtn1 = currentState1;
    Serial.println("test/reset");

    digitalWrite(myLed1, HIGH);
    myStepper.write(180);
    while ( myStepper.moving());// wait for it to finish
    delay(500);
    myStepper.write(-180);
    while ( myStepper.moving());// wait for it to finish
    delay(500);
    digitalWrite(myLed1, LOW);
  }
}/**********************************************************************/

void doButtontest() {
   Serial.println("doButtontest ");
   
  if (myButtons.pressed(btnPins[0]) ) {            // one rotation CW then CCW
    Serial.println("checking cw and ccw ");
    digitalWrite(myLed1, HIGH);
    myStepper.rotate(180);
    delay(500);
    myStepper.rotate(-180);
    digitalWrite(myLed1, LOW);
  }

  if (myButtons.pressed(btnPins[1])) {            // Stop-emegency
    Serial.println("Stop-emergency.. ");
    myStepper.stop();
  }

  if (myButtons.pressed(btnPins[2])) {            // find home - not fiished
    Serial.println("Looking for home.. ");
    myStepper.rotate(180);
    delay(5000);
  }

  if (myButtons.pressed(btnPins[3])) {         // Limit Switch set the zero point  as home
    Serial.println("Home has been set.. ");
    myStepper.setZero();
    digitalWrite(myLed1, HIGH); delay(1000); digitalWrite(myLed1, LOW);

  }
}/**********************************************************************/

type or paste code here

I notice right off the bat that this for loop iterates 8 times, but the btnPins array only has four elements. Once you go out of bounds on memory you're unto undefined behavior. Really anything can happen at that point. Strange errors in completely unrelated parts of the code are typical for out of bounds type problems. So until that is fixed troubleshooting anything else is a fools errand.

BTW: Why analogRead for a button? analogRead is going to treat 2 and A2 as the same thing. You would normally use digitalRead for a button or switch?

Where did you get this part from ?

If you look into the examples that are delivered with the mobatools
these examples use additional code that automtatically calculates the correct numbers

#include <MoToButtons.h>
// define pin numbers
const byte buttonPin [] = { A0, A1, A2, A3 };
const byte anzahlButtons = sizeof(buttonPin); // << calculate correct number of buttons

button_t getHW( void ) {
  // raw reading of buttons
  button_t buttonTemp = 0;
  for (byte i = 0; i < anzahlButtons; i++) {
    bitWrite( buttonTemp,i,!digitalRead(buttonPin[i]) );  // << digitalRead ven for "analog" inputs
  }
  return buttonTemp;
}

MoToButtons Buttons( getHW, 20, 500 );

best regards Stefan

1 Like

I had tried that but the sketch would not compile, the line that does not compile is

const byte btnPins[] = {A1, A2, A3,  2 }; // assign pins to the buttons

I understand that this proceedure is to reassign the analog pins but I have both analog and digital -

Do I need to setup two arrays one for analog and one for digital?

Are you sure that your IDE is adjusted to use Arduino Nano?

This example with IDE adjusted to Arduino Nano compiles just fine

// Show Events of buttons in serial monitor

#include <MoToButtons.h>
// define pin numbers
const byte buttonPin [] = { A0, A1, A2, A3 };
const byte anzahlButtons = sizeof(buttonPin);
char txtBuf[50];

button_t getHW( void ) {
  // raw reading of buttons
  button_t buttonTemp = 0;
  for (byte i = 0; i < anzahlButtons; i++) {
    bitWrite( buttonTemp,i,!digitalRead(buttonPin[i]) ); 
  }
  return buttonTemp;
}

MoToButtons Buttons( getHW, 20, 500 );

void setup()
{
  Serial.begin(115200);
  while(!Serial);       // only for Leonardo/Micro ( mega32u4 based boards 
  
  for (int i = 0; i < anzahlButtons; i++)  {    
    // buttons must switch to Gnc
    pinMode(buttonPin[i], INPUT_PULLUP); 
  }
  Serial.println("Starting loop");
  sprintf( txtBuf, "max. managable buttons: %d", sizeof(button_t)*8 );
  Serial.println( txtBuf );
  Buttons.forceChanged();
}

void loop() {
  //--------------------------------------------------------
  // read and process buttons
  Buttons.processButtons();
  // 
  //--------------------------------------------------------
  // print state of buttons if at least one changed
  if ( Buttons.changed() ) {
    sprintf( txtBuf, "State: %d %d %d %d - ", Buttons.state(0), Buttons.state(1), Buttons.state(2), Buttons.state(3) );
    Serial.print( txtBuf ); Serial.println( Buttons.allStates(),BIN );
  }
  //Buttons.resetChanged();
  // print to serial monitor if an event happens ( pressing or releasing )
  for ( byte btnNbr = 0; btnNbr < anzahlButtons; btnNbr++) {
    if ( Buttons.pressed(btnNbr) ) {
      sprintf( txtBuf, "button %d pressed", btnNbr );
      Serial.println(txtBuf);
    }
    if ( Buttons.released(btnNbr) ) {
      sprintf( txtBuf, "button %d released", btnNbr );
      Serial.println(txtBuf);
    }
    if ( Buttons.longPress(btnNbr) ) {
      sprintf( txtBuf, "button %d pressed long", btnNbr );
      Serial.println(txtBuf);
    }
    if ( Buttons.shortPress(btnNbr) ) {
      sprintf( txtBuf, "button %d pressed short", btnNbr );
      Serial.println(txtBuf);
    }
    //delay(500);

  }

}

Yes everything works out find using the (dotest) - made some changes and now it will compile - however - using the (dobuttontest) it does compile but nothing happens when the buttons are pressed,
Not in your example above you are using only analog pins - I have both analog and digital in m sketch

current code:

/*
   Hardware:
  Arduino Nano (3)
  28BYJ-48 stepper motor (32 steps = one full rotation).
  ULN2003 driver board

   Wiring:
  Pin 8 to IN1 on the ULN2003 driver
  Pin 9 to IN2 on the ULN2003 driver
  Pin 10 to IN3 on the ULN2003 driver
  Pin 11 to IN4 on the ULN2003 driver

  Pin 2 to limit switch (NC)
  (gnd to gnd)

  31.Jan.2024
*/


#include <MobaTools.h>

const int stepRev = 2048;    // steps per revolution ( 1/16 microsteps )-HALFSTEP and set the stepsPerRevolution to 4096
MoToStepper myStepper( stepRev, FULLSTEP );

//Stepper motor -
const int rpm = 12;
const byte iniPin[] = {8, 9, 10, 11};


// sample timing
unsigned long previousMillis = 0;
const long interval = 1000;

// stop positions
const int motorPosition[] {0, 250, 560, 1000};// just for testing

// buttons
const byte btnPins[] = {A1, A2, A3, 2 }; // assign pins to the buttons
const byte btnCnt = sizeof(btnPins);

// read analog inputs and return states for MoToButtons
button_t getHW( void ) {
  // raw reading of buttons
  button_t buttonTemp = 0;
  for (byte i = 0; i < btnPins; i++) {
    bitWrite( buttonTemp, i, !digitalRead(btnPins[i]) ); // << digitalRead ven for "analog" inputs
  }
  return buttonTemp;
}

MoToButtons myButtons( getHW, 20, 500 );

// previous button state
byte prevbtn0 = -1;
byte prevbtn1 = -1;
byte prevbtn2 = -1;
byte prevbtn3 = -1;

// led
const int myLed1 = 3;
/**********************************************************************/
/**********************************************************************/

void setup() {
  Serial.begin(115200);Serial.println("Starting ...  ");

  myStepper.attach( iniPin[0], iniPin[1], iniPin[2], iniPin[3] );/******/
  myStepper.setSpeed(rpm * 10);  // the mobatools expect rpm10

  pinMode(btnPins[0], INPUT_PULLUP);// A1
  pinMode(btnPins[1], INPUT_PULLUP);// A2
  pinMode(btnPins[2], INPUT_PULLUP);// A3
  pinMode(btnPins[3], INPUT_PULLUP);// D2
  pinMode(myLed1, OUTPUT);  digitalWrite(myLed1, LOW);//D3
}/**********************************************************************/

void loop() {

  //doTest();//<----------------- this works just to check that everything is good


  myButtons.processButtons();     
  doButtontest();//<----------------- will be the actual process

}/**********************************************************************/
void doTest() {// perform one rotation in each direction

  int  currentState = digitalRead(btnPins[0]);

  if (currentState != prevbtn0) {
    prevbtn0 = currentState;
    Serial.println("test/reset button 0");
    digitalWrite(myLed1, HIGH);
    myStepper.write(180);
    while ( myStepper.moving());// wait for it to finish
    delay(500);
    myStepper.write(-180);
    while ( myStepper.moving());// wait for it to finish
    delay(500);
    digitalWrite(myLed1, LOW);
  }

  currentState = digitalRead(btnPins[1]);
  if (currentState != prevbtn1) {
    prevbtn1 = currentState;
    digitalWrite(myLed1, HIGH);
    Serial.println("button 1"); delay(1000);
    digitalWrite(myLed1, LOW);
  }
  currentState = digitalRead(btnPins[2]);
  if (currentState != prevbtn2) {
    prevbtn2 = currentState;
    digitalWrite(myLed1, HIGH);
    Serial.println("button 2"); delay(1000);
    digitalWrite(myLed1, LOW);
  }
  currentState = digitalRead(btnPins[3]);
  if (currentState != prevbtn3) {
    prevbtn3 = currentState;
    digitalWrite(myLed1, HIGH);
    Serial.println("limit switch"); delay(1000);
    digitalWrite(myLed1, LOW);
  }
}/**********************************************************************/

void doButtontest() {
  Serial.println("doButtontest ");

  if (myButtons.pressed(btnPins[0]) ) {            // one rotation CW then CCW
    Serial.println("checking cw and ccw ");
    digitalWrite(myLed1, HIGH);
    myStepper.rotate(180);
    delay(500);
    myStepper.rotate(-180);
    digitalWrite(myLed1, LOW);
  }

  if (myButtons.pressed(btnPins[1])) {            // Stop-emegency
    Serial.println("Stop-emergency.. ");
    myStepper.stop();
  }

  if (myButtons.pressed(btnPins[2])) {            // find home - not fiished
    Serial.println("Looking for home.. ");
    myStepper.rotate(180);
    delay(5000);
  }

  if (myButtons.pressed(btnPins[3])) {         // Limit Switch set the zero point  as home
    Serial.println("Home has been set.. ");
    myStepper.setZero();
    digitalWrite(myLed1, HIGH); delay(1000); digitalWrite(myLed1, LOW);

  }
}/**********************************************************************/

add serial printing to debug.

There is not actually any difference. All of the analog pins can be used as digital pins. The only time it actually matters is if you cannanalogRead.

When you say that line doesn't compile, what happens? What's the error message?

You should answer this question. I fear that you may not know that the analog pins can be used as digital pins.