Stepper Motor with Joystick problem

Hello all.

I'm hoping someone can guide me down the right path. I'm fairly new to the microcontroller world and I've been struggling with this project for too long now. After re-writing this code a couple of dozen times, I am looking for help completing it or at least "showing me the light".

The project I'm working requires a stepper motor on a linear rail to be controlled via a joystick. The customer wants to be able to move the joystick slightly to make the carriage move slowly. The more the joystick is pushed, the faster it should go. The carriage needs to home at start-up, then the operator can move the carriage with the joystick. The system will have a limit switch at each end of the linear rail and if the operator accidently trips one of the switches, the carriage/motor is to stop and back off of the switch (25 steps).

I am using an ESP8266 NodeMCU controller, a small logic leveler (to bring the 3.3v signals up to 5v for the stepper driver), a Stepperonline DM556T stepper driver, with a FUYU stepper motor (24v, 2amp) and linear rail package. I am powering joystick through the ESP8266, the switches through a 5v breadboard power board, and a 24v laboratory power supply is powering the motor and driver.

So for starters, the system will home okay but the motor will only move in one direction regardless of joystick direction. I do have control of going from slow to faster speed, but when I release the joystick the motor "slows" to a stop instead of just stopping. I'm not sure where I went wrong. If anyone has any suggestions, I would appreciate them. My code is below with a wiring photo below that.

/*  This code uses an Arduino Uno to operate a NEMA23-sized stepper motor using a joystick and DM556T digital stepper driver.
    The joystick movement is proportional to the speed in which the motor turns (more movement = faster RPM).  */

#include <AccelStepper.h>

// change pins to match your setup.
const byte stepPin = D1;
const byte dirPin = D2;
const byte xJoyPin = A0;
const byte stopDownPin = D6;
const byte stopUpPin = D7;

int stopValUp = 1;
int stopValDown = 1;
int xJoy = 0;
int topSpeed = 2000;
long dirVal = 0;
float highSpeed = 1000;
float highAccel = 500;
float lowSpeed = 125;
float lowAccel = 50;

long initialHome = -1;
long endStopUp;
long endStopDown;

long location1;
long location2;
long location3;
long location4;
long location5;

AccelStepper xStepper(AccelStepper::DRIVER, stepPin, dirPin);

/* ======================================================= */
void setup() {

  Serial.begin(115200);
  xStepper.setMaxSpeed(highSpeed);
  xStepper.setAcceleration(highAccel);

  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(xJoyPin, INPUT);
  pinMode(stopDownPin, INPUT_PULLUP);
  pinMode(stopUpPin, INPUT_PULLUP);

  delay(5);

  xStepper.setMinPulseWidth(20);
  /*  Serial.println(" ");
    Serial.println("Stepper motor is homing....."); */

  // motorHome();

  xStepper.setMaxSpeed(highSpeed);
  xStepper.setAcceleration(highAccel);
}
/* ======================================================= */

/* (((((((((((((((((((((((((((())))))))))))))))))))))))))) */
void loop() {
  Serial.println("1");
  jStick();
}
/* (((((((((((((((((((((((((((())))))))))))))))))))))))))) */

/* _______________________________________________________ */
void motorHome() {
  xStepper.setMaxSpeed(lowSpeed);
  xStepper.setAcceleration(lowAccel);
  while (digitalRead(stopDownPin)) {
    xStepper.moveTo(initialHome);
    initialHome--;
    xStepper.run();
  }
  delay(750);

  xStepper.setCurrentPosition(0);
  xStepper.setMaxSpeed(lowSpeed);
  xStepper.setAcceleration(lowAccel);
  initialHome = 50;
  xStepper.moveTo(initialHome);
  xStepper.run();
  initialHome = -1;
  delay(750);

  xStepper.setMaxSpeed(75);
  xStepper.setAcceleration(50);

  while (digitalRead(stopDownPin)) {
    xStepper.moveTo(initialHome);
    initialHome--;
    xStepper.run();
  }

  while (!digitalRead(stopDownPin)) {
    xStepper.moveTo(initialHome);
    xStepper.run();
    initialHome++;
  }
  delay(750);

  xStepper.setCurrentPosition(0);
  Serial.println("Homing Completed");
  Serial.println("");
  xStepper.setMaxSpeed(highSpeed);
  xStepper.setAcceleration(highAccel);
}
/* _______________________________________________________ */

/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   This function reads the limit switches and joystick value.  After reading the switches, sends
   the code to the checkStop function where it will be determined whether or not the motor has
   tripped a switch and if so will stop the motor and move off of the stop 25 steps.
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */

void jStick() {

  Serial.println("2");

  checkStop();                                /* checkStop function will handle any limit switches
                                                 that are tripped.  If limit switches are fine, code
                                                 will continue and motor will run based on Joystick values */
  if (stopValUp == 1 && stopValDown == 1) {
    xJoy = analogRead(xJoyPin);               /* reads the joystick values */

    if (xJoy > 520 && xJoy < 550) {           /* if joystick is @ neutral postition, motor does not run */
      dirVal = 0;
      moveMotor();
    }

    if (xJoy < 521 || xJoy > 550) {           /* if joystick is out of neutral position....  */

      xStepper.setMaxSpeed(lowSpeed);         /* set speed and acceleration to slow speed */
      xStepper.setAcceleration(lowAccel);
      if (xJoy < 521 && xJoy > 150) {         /* steps motor once relative to current position */
        dirVal = (xStepper.currentPosition() - 1);
        moveMotor();
      }
      if (xJoy > 550 && xJoy < 850) {         /* steps motor once relative to current position */
        dirVal = (xStepper.currentPosition() + 1);
        moveMotor();
      }
    }

    if (xJoy < 151 || xJoy > 849) {
      /* if joystick is located all of the way to one side
                                                   this 'if' statement will increase the speed of the motor */
      xStepper.setMaxSpeed(highSpeed);
      xStepper.setAcceleration(highAccel);
      if (xJoy < 151) {

        dirVal = (xStepper.currentPosition() - 1);
        moveMotor();
      }
      if (xJoy > 849) {

        dirVal = (xStepper.currentPosition() + 1);
        moveMotor();
      }
    }           /* return to loop */
  }
}
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   This function moves the stepper motor one step
   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

void moveMotor() {

  Serial.println("3");

  xStepper.move(dirVal);
  xStepper.run();
}
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/* **************************************************************************************
   This function will determine if either limit switch has been tripped.  If one has been
   tripped, the function stops the motor, changes it's direction, and decreases the speed.
   The code is then send to the 'moveFromStop' function.
   ************************************************************************************** */

void checkStop() {

  Serial.println("4");

  stopValUp = (digitalRead(stopUpPin));       /* reads limit switches */
  stopValDown = (digitalRead(stopDownPin));

  if (stopValUp == 0 || stopValDown == 0) {
    dirVal = 0;
    moveMotor();
    delay(500);
    xStepper.setMaxSpeed(lowSpeed);
    xStepper.setAcceleration(lowAccel);

    moveFromStop();
  }
}
/* ******************************************************* */


/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
   This function moves the carriage 25 steps from the limit switch that was tripped.
   +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */

void moveFromStop() {

  Serial.println("5");

  if (stopValUp == 0) {
    endStopUp = xStepper.currentPosition();
    dirVal = (endStopUp - 25);
    xStepper.moveTo(dirVal);
    xStepper.run();
    delay(500);
  }

  if (stopValDown == 0) {
    endStopDown = xStepper.currentPosition();
    dirVal = (endStopDown + 25);
    xStepper.moveTo(dirVal);
    xStepper.run();
    delay(500);
  }

  jStick();
}
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++ */

I do not see anywhere in your program where you write to dirPin. Probably missed it. Are you following the timing diagram for changing the value of dirPin?
Paul

I see a picture of a small-signal I2C level converter.
This is AFAIK not suitable to drive the optos of the stepper driver.
Leo..

That needs to be

  xStepper.moveTo(initialHome);
  while (xStepper.distanceToGo() != 0)
    xStepper.run();
  initialHome = -1;

No delays when using AccelStepper, it needs to progress, so run() must be called repeatedly.

For the joystick management I'd read the joystick value, convert it to a step-rate, and then use that step rate to schedule xStepper.move () calls with a +1 or -1 argument depending on direction. All the while having the run() method called. So something like:

void loop ()
{
  int joy_value = analogRead(xJoyPin) ;
  int step_rate = convert (joy_value) ;   // you need to define this, nominally steps/second
  static unsigned int last_step = 0 ;
  if (step_rate != 0)
  {
    int millis_between_steps = 1000 / abs(step_rate) ;
    if (millis() - last_step >= millis_between_steps)  // regularly step
    {
      last_step += millis_between_steps ;
      xStepper.move (step_rate < 0 ? -1 : +1) ;
    }
  }
  else
  {
    last_step = millis() ;  // while not jogging keep last_step current
  }
  xStepper.run() ;  // must call regularly
}

Hi Paul_KD7HB.

Thank you for replying. I was using the joystick values to determine direction. Anything below the "neutral" numbers would be driven by a -1 and anything above would get driven by a positive 1. I've tried using the direction pin in one of the other 23 revisions of this program that I've tried and I can't seem to get it to work with the AccelStepper library. I'm sure it has something to do with my code, but I had no success with it.

Ron.

Then it is time to start over and find the problem. Write a quick program to turn the pin you re using for the direction high for 2 seconds and then low for two seconds. That is all it does. Then use your DVM to see if that pin really goes high and then low. That will eliminate any possibility of that failing.
Paul

Hi Wawa.

Appreciate the reply. The item description (from when I purchased it) said it was a logic level converter. I'm kind of ignorant when it comes to the terminology of this field, so forgive me if I called it something that it wasn't. It's not running off of the I2C channels/library and I'm not sure what a "signal I2C level converter is". I'm putting 3.3v into it with the controller signals going to it and I've got 5v feeding the other side with the signal wires going to the driver.

Ron.

Hello MarkT.

I appreciate all of the info and input. I realize delays are not good for the running of stepper motors, but this was in the "Set-Up" part of the code and I wanted to make sure the system was "ready" when it entered the loop. When using the 'distanceToGo()' function, what is the actual distance to go? Do I have to populate this somehow?

So, again forgive my ignorance, but what is the 'convert()' function converting the joystick values to? I've tried doing some quick research on the function, but I can't find anything on it. Also, where in you sample does it take that value and know what direction to go?

Thank you again.
Ron.

Paul,

Okay. I'll give it a go and get back to you.

Thank you,
Ron.

Paul,

Okay, so when I print to the serial monitor the value of 'dirPin' (it is supposed to print out every time it switches directions) it reads '4' every time. (see screen shot)

dirPin

Sorry Paul.... this is my code:

#include <AccelStepper.h>

// change pins to match your setup.
const byte stepPin = D1;
const byte dirPin = D2;

AccelStepper xStepper(AccelStepper::DRIVER, stepPin, dirPin);

/* ======================================================= */
void setup() {

  Serial.begin(115200);
  xStepper.setMaxSpeed(highSpeed);
  xStepper.setAcceleration(highAccel);

  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);

  delay(5);
}

void loop() {
  digitalWrite(dirPin, HIGH);
  Serial.println("");
  Serial.print(dirPin);
  Serial.println("");
  delay(2000);

  digitalWrite(dirPin, LOW);
  Serial.println("");
  Serial.print(dirPin);
  Serial.println("");
  delay(2000);
}

Paul,

I just checked at the driver with a voltmeter and it is pulsing power to the direction pin every 2 seconds.

You should have posted a web link to the item.

I think you are trying to drive the opto couplers inside the driver with the 10k pull up resistors of an I2C level converter (Fritzy, post#1), which most likely won't work.
Leo..

YOU ARE PRINTING THE PIN NUMBER, NOT THE VALUE. Just print "HIGH" or print "LOW". What does your DVM show when you print whether the pin is supposed to be high or supposed to be low.
Paul

I'm sorry, Paul. What is a DVM? How do I ask for the 'Value' of the pin?

I know these are probably simple/dumb questions to you, but I only know what I know.....

Wawa,

I was wrong. It does have something to do with I2C, but it also converts 3.3v to 5v???

" 4 Channels IIC I2C Logic Level Converter Bi-Directional Module 3.3V to 5V Shifter for Arduino"

https://smile.amazon.com/KeeYees-Channels-Converter-Bi-Directional-Shifter/dp/B07LG646VS/ref=sr_1_3?crid=1I5QUH6G7SJ7I&keywords=logic+level+converter+3.3v+to+5v&qid=1639714092&sprefix=logic+level%2Caps%2C88&sr=8-3

DVM = Digital Volt Meter. A plain old analog will also do the job.
Or even a red LED in series with a 220 ohm resistor, connected the right way, will light up when the pin is HIGH.
Paul

I2C level shifters can sink the same current as the MCU pin, but can only source the current of the pull up resistor on that board.
You are using the board in "source" mode, which is (5volt - 1.2volt opto LED) / 10k = <0.4mA.
The opto LED expects (5 - 1.2) / 270ohm (inside driver) = 14mA...

You could try to use the level shifter in "sink" mode (never tried that myself).
Connect step(+) and direction(+) of the driver to 5volt, as well as HV of the level shifter.
Connect step(-) and direction(-) of the driver to HV1 and HV2.
Connect LV to 3.3volt of the NodeMCU, and LV1/LV2 to it's output pins.
Opto LED (inside the stepper driver) drive current is now limited to what the NodeMCU pin can supply, and that might not be the full 14mA. But it might work.
Leo..

Paul/Leo,

Thank you again. I have to get up in 4.5 hours, so I'm heading to bed. I'll be back at this tomorrow night. I'll let you know what I come up with then.

Both of your guys' time is much appreciated.
Ron.

P.S.... Paul, my multimeter showed 2.6 volts pulsing to the driver direction pin every 2 seconds.

Was that 2 seconds on and two seconds off, or a pulse every two seconds where the + was less than two seconds. Even so, the DVM may not be able to respond that quickly. I sometimes get frustrated with mine not showing a voltage immediately! So, your pin and code are working as they should be. Time to track the signal further.
Paul