Arduino MPG Standalone JOG

Hello guys! I’m pretty new to arduino and code, I’m trying to make a stand-alone jog controller for my project, manual pulse generator / encoder

I’ve found some code and tried different approaches and it seems to be working pretty good,

The problem I’m having is=

I want the mpg handwheel (100p/r) to turn the stepper 1 revolution, and that works.

But then I wanted a switch/button to change the output pulses to double the jog speed,
This is the code the button will have to change

steps_per_pulse 4 (1rev) to
steps_per_pulse 8 (2rev)

It also works! But when I’m switching and starting to rotate the mpg the motor starts to spin uncontrollably for a few secounds at full speed and after that it works at set jog rate? It happens every time I switch

The code is probably not right at all! That’s why I’m asking for help, and maybe to learn something new!

#include <AccelStepper.h>
 
#define encoder_pin_A 8
#define encoder_pin_B 9
int encoder_pin_A_last = LOW;
int encoder_pos = 0;
int n = LOW;
/////////////////////////////////////////////////////////////////////
const int buttonPin = 2;     // the number of the pushbutton pin
int buttonState;             // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers
/////////////////////////////////////////////////////////////////////
#define stepper_pin_step 6
#define stepper_pin_dir  5
 
// Enc have 100 steps per revolution
// The motor have 400 steps per revolution
// Want: 1 encoder rev = 1 stepper rev
// 400 / 100 = 4...
float steps_per_pulse = 4;
 
AccelStepper stepper(AccelStepper::DRIVER, stepper_pin_step, stepper_pin_dir);
 
void setup() {
  stepper.setMaxSpeed(3000.0);
  stepper.setAcceleration(30000.0);
   
  pinMode(encoder_pin_A, INPUT_PULLUP);
  pinMode(encoder_pin_B, INPUT_PULLUP);
  pinMode(buttonPin, INPUT);

}
 
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();
////////////////////////////////////////////////////////////////////////
// read the state of the switch into a local variable:
  int reading = digitalRead(buttonPin);

  // check to see if you just pressed the button
  // (i.e. the input went from LOW to HIGH), and you've waited long enough
  // since the last press to ignore any noise:

  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer than the debounce
    // delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;

      // only toggle the LED if the new button state is HIGH
      if (buttonState == HIGH) {
        steps_per_pulse = 8;
      
      } else { 

        steps_per_pulse = 4;        
      }
    }
  }

  // save the reading. Next time through the loop, it'll be the lastButtonState:
  lastButtonState = reading;
}

How is it connected/wired?

At the moment just pressed into 5v or gnd on the breadboard

Also tried

pinMode(buttonPin, INPUT_PULLUP);

That's highly wanted! Else the controller will be floating picking up any influencing electric field in the air when the button is not pressed.

Yes i tried that, but no difference..

I can’t figure out why the motor spins at full speed without any encoder signal (buffer should be empty)

I only happens when I change state of the button, could be electromagnetic interference due to the motor/driver being that close to the wires/arduino but I doubt that,

Maybe add code for delay/pause when the state of the button changes? I cloud upload a video on YouTube or something and show it in action?

Without any signal connected..... That could very well be a great mistake. If You disconnect signals You need to connect those emptied pins to gnd, or Vcc. Leaving pins without a defined voltage level is really bad practise, asking for trouble.
Start using Serial monitor and serial.print in the code to tell what's going on inside there.

Yes it’s not a good practice having floating I/Os The A & B connections are always connected, but the encoder itself maybe doesn’t have pull up/down resistor built in, I’ll try a pull down/up resistor tied to both A-B (D8 - D9)

Here’s what’s going on,

Did just realize that those inputs already has built in pullup,

Know that Arduinos do take som time before executing Your code, after powered up or resetted. In order to avoid unintended action during that internal work measures might have to be taken.

Any change?

No difference, gonna try to add serial monitor to monitor the state of the encoder input and button change

Do that!
That technic saved me during many years when I faced large unknown systems. Very effective way to debug!

Tried looking for the states, no jittery ones and zeros there, there must be something in the code who does this

Keep on checking internal variables.

No success, all the numbers shown in serial monitor seems to behave as expected,

Is counting but do never reset, in either way with or without pull-up it counts as I should,

I also tried to delete all existing code for the push button and edit in new code with delays and such without any success, same problem when changing the value while powered on,

Hi,
How about a circuit diagram?
Please no Fritzy or Kicad pictures.
I have viewed the clip, but a diagram will make a big difference.

How are you powering the stepper?
How are you powering the Arduino?
What is the stepper, link to specs.
What is the encoder, link to specs.

Tom.... :smiley: :+1: :coffee: :australia:

Hi! I’ll make a diagram later, why no fritzing diagram?

Not much information to be found about the Chinese mpg, it has 4 connections

VCC, 0V, A, B. Produces 100 pulses per rev,

Stepper motor and driver is also Chinese, I’ll try later to use another drive and stepper

Also tried to share ground with 24V powersupply and the Arduino without success

I have just started playing with something similar to this and learning the Accelstepper functions.
My project is a self leveling plate function which is working really well, I set up my CW5045 controller to 250 microstepping for what I need.
This is the testing code that I've set up and using.
Hopefully you will get to understand it,
It give serial data out when enabled, Just change it to zero when when not needed.
You can change the step counter multiplier values (I've set mine to 1,10,50,100).
You just press the encoder button and it steps it up to 100 then back to 1.
There is a denounce function to take care of the encoder button.
When you turn the rotary encoder clockwise it increase the steps and moves the motor in the clockwise direction on each click and When you turn the rotary encoder anti-clockwise it decreases the steps and moves the motor in the anti- clockwise direction on each click

//AccelStepper
#include <AccelStepper.h>
#include <Rotary.h>
AccelStepper stepper(1, 8, 9);// direction Digital 9 (CCW), pulses Digital 8 (CLK)
#define stepPin3 3                    // Set 'Step' rotary encoder pins, Interrupt pin 0 must be used
#define stepPin4 2                    // Set 'Step' rotary encoder pins,Interrupt pin 1 must be used
#define ECHO_TO_SERIAL 1 //change to zero = no serial output
const int Step_multiyplier = 6; //SW pin on the rotary encoder (Button function)
//Defining variables
int ButtonCounter = 0;
volatile int RotateCounter = 0;
Rotary Volts_Rotary = Rotary(stepPin3, stepPin4);
//Statuses
int stepperRotarySteps; // This keeps track of the steps done with the rotary encoder

//###################
const byte Step_multipliers[] = { 1, 10, 50, 100 }; //This moves the stepper motor by 1,10,50,100
const byte stepCount = sizeof(Step_multipliers) / sizeof(Step_multipliers[0]); //holds the multipliers
volatile byte currentStep = 0;
//###########################################
//# PRINT DATA TO SERIAL MONITOR IF ENABLED #
//###########################################
void printRecord(Print* pr, char sep = ',') {
  pr->print("STEP MULTUPLIER: "); // Print btye 0
  pr->print(Step_multipliers[currentStep]);
  pr->print(sep);
  pr->print("STEPPING COUNTER: ");
  pr->print(RotateCounter);
  pr->println();

}
void setup()
{

  Serial.begin(115200);
  pinMode(stepPin3, INPUT_PULLUP);     // Pins for step rotary encoder on analogue pins A2, A3
  pinMode(stepPin4, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(stepPin3), Volts_encoder, CHANGE);
  attachInterrupt(digitalPinToInterrupt(stepPin4), Volts_encoder, CHANGE);
  pinMode(Step_multiyplier, INPUT_PULLUP);
  stepper.setMaxSpeed(1000); //SPEED = Steps / second
  stepper.setAcceleration(500); //ACCELERATION = Steps /(second)^2

}

void loop()
{
#if ECHO_TO_SERIAL // Send debug to serial port if enabled
  printRecord(&Serial); //Send the data to print function
#endif //ECHO_TO_SERIAL
  Buttons() ; //Check the roatry encdoe button
  RunTheMotor(); // run the motors
}

void RunTheMotor() //function for the motor
{
  stepper.enableOutputs(); //enable pins
  stepper.moveTo(RotateCounter); //-1 is to match the rotation of the encoder with the rotation of the stepper
  while (stepper.distanceToGo() != 0)
  {
    stepper.runToNewPosition(RotateCounter);
  }

}
void Volts_encoder()   {
  unsigned int result = Volts_Rotary.process();
  ///Rotary encoder 1 for setting the volts
  if (result) {
    if (result == DIR_CW) {
      RotateCounter += Step_multipliers[currentStep] ;
    } else {
      RotateCounter -= Step_multipliers[currentStep] ;
    }
  }
}
void Buttons() {
#define buttonPressed LOW                             // When the button is pressed the input will be low, this is to remove the confusion this migth cause.
  uint32_t currentMillis = millis();                    // Millis times uses to debounce the button
  static uint32_t lastMillis;                           // Start of the debounce timeout
  const uint32_t BOUNCETIMEOUT = 20;                    // Debounce time in milliseconds
  bool currentButtonState = digitalRead(Step_multiyplier);     // Reads the current state of the button and saves the result in a bool
  static bool lastButtonState;                          // Holds the previous debounced state of the button

  if (lastButtonState != currentButtonState) {          // Checks to see if the button has been pressed or released, at this point the button has not been debounced
    if (currentMillis - lastMillis >= BOUNCETIMEOUT) {  // Checks to see if the state of the button has been stable for at least bounceTimeout duration
      lastButtonState = currentButtonState;             // At this point the button has been debounced, so save the last state
      if (currentButtonState == buttonPressed) {
        currentStep++;
        if ( currentStep >= stepCount ) currentStep = 0;
      }
    }
  } else {
    lastMillis = currentMillis;                         // Saves the current value of millis in last millis so the debounce timer starts from current millis
  }
}

You may have to play around with the pins depending on how yours is wired and change the speeds to suit yours.

Let us know if you try it or get on with it