Direct control of a stepper motor using a CNC Jog Wheel and 2 limit Switches

Hello, I need help modifying implementing into this code a part that enables me to use 2 limit switches MAX-MIN, the idea behind it is that when the limit switch is activated MIN or MAX the stepper motor stops rotating in that direction, and only allows rotation in the opposite direction. It is for a motorized transformer variac project.

Option2-Implementing a home procedure and then limiting the maximum steps would be an option too.

#define encoder_pin_A 8
#define encoder_pin_B 9
int
encoder_pin_A_last = LOW;
int
encoder_pos = 0;
int
n = LOW;
#define stepper_pin_step 6
#define stepper_pin_dir  5
// Enc have 24 steps per revolution
// The motor have 800 steps per revolution
// Want: 1 encoder rev = 1 stepper rev
// 800 / 24 = 33.3333333333...
float
steps_per_pulse = 33.3333333333;
AccelStepper stepper(AccelStepper::DRIVER, stepper_pin_step, stepper_pin_dir);
void
setup() {
stepper.setMaxSpeed(800.0);
stepper.setAcceleration(300.0);
pinMode(encoder_pin_A, INPUT_PULLUP);
pinMode(encoder_pin_B, INPUT_PULLUP);
}
void
loop() {
// read encoder
n = digitalRead(encoder_pin_A);
if
((encoder_pin_A_last == LOW) && (n == HIGH)) {
if
(digitalRead(encoder_pin_B) == LOW) {
encoder_pos--;
} 
else
{
encoder_pos++;
}
// set stepper to the new calculated position
stepper.moveTo((
long
) round(encoder_pos*steps_per_pulse));
}
encoder_pin_A_last = n;
stepper.run();
}

Why do you want to use a stepper motor if you control it with a rotary encoder? With a DC motor it's easy to restrict the movement by limit switches, without any special code required.

And with a stepper and one limit switch to home to it is easy to count steps and limit movement. No encoder required. Unless the motor misses steps which if designed right should not happen.

Sorry I did a mistake a meant CNC Jog Wheel.

Implementing a home procedure and then limiting the maximum steps would be an option too. I really don't know how to implement it into this code if you could help me it would be nice.

I think that I understand your project better. I wired up my Uno with a stepper and rotary encoder and your code works OK to position the motor.

What kind of limit switch do you have (mechanical, optical, Hall effect, ... )?

It is late here, but I can work with you some tomorrow if you would like.

1 Like

i would like that. See you tomorrow.

1 Like

You only need one. Probably where the wiper on the Variac is at minimum?

1 Like

So I have 1 mechanical limit switch at a minimum, and I removed the second one.

Did you have any success?

I have a code that works on my setup. You can change the pin numbers and try it out and modify, adjust or use the parts that you want. I changed to the Encoder library for the rotary encoder as it does debounce and seems more consistent. The Encoder library is available through the IDE library manager.

The limit switch on my setup is a normally open (pulled high) reflective opto sensor that goes LOW when the motor is at home. A normally open mechanical switch or Hall effect switch should work with no changes.

#include <AccelStepper.h>
#include <Encoder.h>

const int MIN_LIMIT = 0;
const int MAX_LIMIT = 45;

const byte encoder_pin_A = 9;
const byte encoder_pin_B = 10;
const byte stepper_pin_step = 2;
const byte stepper_pin_dir  = 5;
const byte limitSwPin = 11;

// Enc have 24 steps per revolution
// The motor have 800 steps per revolution
// Want: 1 encoder rev = 1 stepper rev
// 800 / 24 = 33.3333333333...
float steps_per_pulse = 33.3333333333;
bool isHomed = false;

AccelStepper stepper(AccelStepper::DRIVER, stepper_pin_step, stepper_pin_dir);
Encoder myEnc(encoder_pin_A, encoder_pin_B);

long oldPosition  = -999;
int divisor = 4; // divide by divisor to get one count/click on encoder

void setup()
{
   Serial.begin(115200);
   stepper.setMaxSpeed(2000.0);   
   stepper.setAcceleration(1000.0);
   
   pinMode(limitSwPin, INPUT_PULLUP);

   delay(10);
   home();
   Serial.println("homed");
}

void loop()
{
   if (isHomed)
   {
      long newPosition  = myEnc.read() / divisor; // divide by divisor to get one count/click
      
      // bound checking
      if (newPosition >= MAX_LIMIT)
      {
         newPosition = MAX_LIMIT;
         myEnc.write(MAX_LIMIT * divisor);
      }      
      
      if (newPosition < MIN_LIMIT)
      {
         newPosition = MIN_LIMIT;
         myEnc.write(MIN_LIMIT);
      }
           
      if (newPosition != oldPosition)
      {
         Serial.print("pos = ");
         Serial.println(newPosition);
         // set stepper to the new calculated position
         stepper.moveTo(newPosition * steps_per_pulse);
         oldPosition = newPosition;
      }
      stepper.run();
   }
}

void home()
{
   while (digitalRead(limitSwPin))
   {
      stepper.move(-5);
      stepper.runSpeedToPosition();
   }
   isHomed = true;
   stepper.setCurrentPosition(0); // homed
   myEnc.write(0);
}

If you have questions or concerns, don't hesitate to ask. I should be here most of the day.

1 Like

Thank you very much this is simply wizardry for me, When the project progresses I would add some additional functionality. I will contact you in the future.

1 Like

Hello sir could you give me an example of how to implement a Homing switch for the code.

The code will work with any active low switch. Could be a mechanical micro switch, a reed switch, a Hall effect switch or optical switch. As long as it reads HIGH when open and LOW when closed.

I am talking about an additional switch that has a function to home the motor to MIN_LIMIT.
But I don't know how to update the (pos = to 0) after the motor is homed.

#include <AccelStepper.h>
#include <Encoder.h>
#include <ezButton.h>

const int MIN_LIMIT = 0;
const int MAX_LIMIT = 100;

const byte encoder_pin_A = 11;
const byte encoder_pin_B = 10;

const byte stepper_pin_step = 9;
const byte stepper_pin_dir  = 8;

const byte limitSwPin = 7;

int led_minmax = 5;
int led_move = 6;

float steps_per_pulse = 44.7;
bool isHomed = false;

ezButton button(4);
AccelStepper stepper(AccelStepper::DRIVER, stepper_pin_step, stepper_pin_dir);
Encoder myEnc(encoder_pin_A, encoder_pin_B);

long oldPosition  = -999;
int divisor = 4;

void setup()
{
   button.setDebounceTime(50);
  
   Serial.begin(115200);
   stepper.setMaxSpeed(1000.0);   
   stepper.setAcceleration(800.0);
   
   pinMode(limitSwPin, INPUT_PULLUP);
   pinMode(led_minmax, OUTPUT);
   pinMode(led_move, OUTPUT); 
   
   delay(10);
   home();
   Serial.println("homed");
}

void loop()
{
   Homed(); 
   PositionLed();
   HomeSw();
}

void Homed() 
{
   if (isHomed)
   {
      long newPosition  = myEnc.read() / divisor;
      if (newPosition >= MAX_LIMIT)
      {
         newPosition = MAX_LIMIT;
         myEnc.write(MAX_LIMIT * divisor);
      }      
      
      if (newPosition < MIN_LIMIT)
      {
         newPosition = MIN_LIMIT;
         myEnc.write(MIN_LIMIT);
      }
           
      if (newPosition != oldPosition)
      {
         Serial.print("pos = ");
         Serial.println(newPosition);
         stepper.moveTo(newPosition * steps_per_pulse);
         oldPosition = newPosition;
      }
      stepper.run();
   }
}

void PositionLed() 
{
   int Positio = (oldPosition);
   if(Positio == 0 || Positio == 100)
   {
      digitalWrite(led_minmax, HIGH);
      digitalWrite(led_move, LOW);
   } 
   else 
   {
      digitalWrite(led_minmax, LOW);
      digitalWrite(led_move, HIGH);  
   }
}

void HomeSw() 
{
  button.loop();
  if (button.isPressed()) 
  {
     stepper.moveTo(MIN_LIMIT);
     Serial.println("GoHome");
}
}


void home()
{
   while (digitalRead(limitSwPin))
   {
     stepper.move(-500);
     stepper.runSpeedToPosition();
   }
   isHomed = true;  
   stepper.setCurrentPosition(0);
   myEnc.write(0); 
}
``````````````````````````````````````````````````````````````````````````````````````````````````````````````

There's a bug if you turn the encoder to 100 and continue turning it jumps from 99 -100-99
Can you help me fix it?

This sets AccelStepper's step count.

This zeros the encoder

I do not see that on my setup. Once it hits 100 it just sits there as I turn the encoder knob. Try putting a 0.1uF cap each from the A terminal and the B terminal of the encoder to ground.

I added a button 2 loop and it works great. Tenx

void HomeSw() 
{
  button1.loop();
  button2.loop();
  if (button1.isPressed()) 
  {
     stepper.moveTo(MIN_LIMIT);
     Serial.println("GoHome");
  }
  if (button2.isPressed()) 
  {
     stepper.setCurrentPosition(0);
     myEnc.write(0);
     Serial.println("Homed");
  }
}

I added 2 capacitors and it did not fix the problem no idea what can it be.