Analog joystick controlling 2 steppers: can't get setSpeed() to set the speed?

Seeking help understanding WHY I am getting the results I am getting from my code.

Hardware:
Arduino Uno rev3, duinotech XC4422 joystick, 2 Duinotech XC-4492 motor controller modules, and 2 NEMA 17 stepper motors.

Wires:
Joystick to Arduino on analog 0 and 1, and using 5v and one of two ground sockets on the same row of connectors.
Arduino to first motor controller on digital pins ~3,4,~5,6 to controller pins IN4, IN3, IN2, IN1, in that exact-match order.
Arduino to second motor controller on pins 7,8,~9,~10 to controller pins IN4, IN3, IN2, IN1, again in order.
Each motor controller wired to it's own 4-wire stepper motor in identical patterns.

Prior to attempting to add speed control, this code WORKED. The joystick more or less functioned the same way a set of four switches would (either MOVING or NOT MOVING, at the initially set speed), but it WORKED. Speed was set to 20, and both steppers went both directions as instructed by joystick movement at that fixed speed.

I looked at a lot of posts/web pages/etc about speed control from analog input on arduinos and pretty much everything seemed to be related to some specific driver or other specific hardware. It also all seemed over-done (way too much code to do a basic thing).

So I thought a simple combo of the map() to change scale and abs() to kill negatives would get me a 0 to 100 speed scale pretty easy and then I could feed the result into the stepper instance. (See code)

Um, nope.

Just focusing on the Y axis for the following:

The map() is correctly converting between the scales and outputting to the variable.
Serial printing lets me see that this is so.

But, I am trying to use StepperY.setSpeed(YSpeed) to get the damn speed value INTO the stepper instance, and I can't. The speed is totally stuck at 20.

So please, if you can, help me understand why this method of passing the value stored in YSpeed to the actual instance of the stepper driver is not working. I'm seriously missing something, and I don't know what it is that I AM missing!

Seriously - is this all because of how I am sending the step(1) instructions at the bottom and not even related to the speed code I wrote?

I have no idea at this point and am plenty confused.

Thanks.

Code follows (Yes I know X-axis speed code is not good.)

//  First stab at using an Arduino to control a light fixture's direction of projection.
//  Joystick control of two stepper motors.
 
#include <Stepper.h>
                                    
// initialize stepper instances for each axis' motor control:
Stepper StepperY(200, 2,3,4,5);
Stepper StepperX(200, 6,7,8,9); 

// Why the F*** doesn't the stepper.h documantation tell us HOW it uses these four pins - as in "which pin is doing what"... Ugh. No Sh** they go to the motor, you boobs.


//________________________________________________________________________________________
 
void setup() {  

  // run the serial monitor so I can print to the computer's screen
  Serial.begin(9600); 

  // Now set the initial speed of the motors
  // I really don't know why I am setting this here versus up in the creation of the instance above or left undeclared until assigned in the loop. 
  // but it was done in void setup in the sample code I saw so I am putting it here.
  
  StepperX.setSpeed(20);   // set first stepper speed
  StepperY.setSpeed(20);   // set second stepper speed
}
 

//________________________________________________________________________________________

void loop() {

  // Find out where the joystick is 'naturally' sitting and set 
  // starting values where joystick movement will trigger resposes 
  // from the Arduino and add a small padding to disallow jitter at the natural dead zone
  // The joystick is NOT at the same value every time you power up the arduino, 
  // so I needed this 'grab and set' routine to compensate.
  // 'static' is used so these don't recalculate every loop cycle.
  
  static int XnegStart= (analogRead(A0)-10);
  static int XposStart = (analogRead(A0)+10);
  static int YnegStart = (analogRead(A1)-10);
  static int YposStart = (analogRead(A1)+10);

  
  // set up speed values by mapping the joystick analog input from 0 to 1023 scale against a 0 to 100 scale to generate a useful speed value
  // NOTE- I am unsure about my stepper motor's actual max RPM so am being very conservative with this 100 RPM scale
/*
  int XSpeed = analogRead(A0);
  XSpeed = map(XSpeed,0,1023,-100,100);
  if ( abs(XSpeed) < 20 ) {XSpeed=20;}
    else ( StepperX.setSpeed(abs(XSpeed)) );
 */ 
  int YSpeed = analogRead(A1);
  YSpeed = abs(map(YSpeed,0,1023,-100,100));
  if ( YSpeed < 20 ) { YSpeed = 20; }
  if ( YSpeed > 20 ) { StepperY.setSpeed(YSpeed); }

  
  // grabbing values from joystick so I don't call analogRead a ton of times in the if statements below
  int JoystickXinput = analogRead(A0); 
  int JoystickYinput = analogRead(A1); 


///*
  // Print to screen hoping to see what he hell is going on here 
//  Serial.println("X");  
//  Serial.println(JoystickXinput);
//  Serial.println(abs(XSpeed));
  Serial.println(" ");
  Serial.println(" ");
  Serial.println("Y");
  Serial.println(JoystickYinput);
  Serial.println(YSpeed);
  Serial.println(" ");
  Serial.println(" ");
//*/

  
  //  Make steppers move !        
  if (JoystickXinput < XnegStart) { StepperX.step(1); }   // step X left
  if (JoystickXinput > XposStart) { StepperX.step(-1); }  // step X right
  if (JoystickYinput < YnegStart) { StepperY.step(1); } // step Y up
  if (JoystickYinput > YposStart) { StepperY.step(-1); } // step Y down
 
 }

Oh, I HAVE tried sending the YSpeed variable as a long not an int. No change.

// Why the F*** doesn't the stepper.h documantation tell us HOW it uses these four pins - as in "which pin is doing what"... Ugh. No Sh** they go to the motor, you boobs.

They do NOT go to the motor. They go to the motor driver. And since you have not said WHICH motor driver, you can't possibly expect anyone to tell you where.

  if ( YSpeed < 20 ) { YSpeed = 20; }
  if ( YSpeed > 20 ) { StepperY.setSpeed(YSpeed); }

If the value in YSpeed is 14, you change the value to 20. 20 is not greater than 20, so whatever the previous speed was remains the speed that the stepper should step at. Why?

  // grabbing values from joystick so I don't call analogRead a ton of times in the if statements below
  int JoystickXinput = analogRead(A0);
  int JoystickYinput = analogRead(A1);

You've already read them once. Why do you need to read them again?

On each pass through loop(), you MIGHT, or might not, make the stepper step ONCE. How you expect to control it's speed when stepping ONE STEP AT A TIME is beyond me.

The set speed is useful ONLY when you tell the stepper to step a bunch of times, and then call the run() method to allow the stepper to decide if it is time to step.

First off, I am ignorant not stupid. I am totally new to Arduino and haven't written any meaningful code in well over a decade. I posted here because I don't understand what I did wrong here, and I already acknowledged that I don't know why it is wrong.

Responding to your post:

"They do NOT go to the motor. They go to the motor driver. And since you have not said WHICH motor driver, you can't possibly expect anyone to tell you where."

  1. OK, sorry - sloppy language use. What I wrote is true in a casual sense, though not in a literal one. Point taken.

  2. I did indeed say which motor driver. Top of my post, where I listed all the hardware used.

"If the value in YSpeed is 14, you change the value to 20. 20 is not greater than 20, so whatever the previous speed was remains the speed that the stepper should step at. Why?"

The map() returns values from 0 to 100. I did that to make sure the value passed to the stepper instance is never less than 20, because I want no slower movement than that. It looks clumsy to me but it does work to restrict values to the 20-100 range.

"You've already read them once. Why do you need to read them again?"

Good point. Thanks. Inefficient. But while it IS inefficient it has zero bearing on my posted issue, does it? It isn't causing my issue, right? Do you also critique small children's poor realism in their stick-figure artwork when they show you? Dude, I know this is not any awesome example of code.

MOST IMPORTANT PART:

"On each pass through loop(), you MIGHT, or might not, make the stepper step ONCE. How you expect to control it's speed when stepping ONE STEP AT A TIME is beyond me"

As I said in my post, I am missing something. So thanks for indicating that making them move like that isn't a good idea - but you've done nothing at all to help me understand WHY this is stupid.

My thinking was:
I am not sending the pair of steppers to a COORDINATE on an X/Y axis, I am asking them to move only when sent a signal. So having them move many steps didn't seem to serve that purpose to me. In order to create a 'number of steps' to send, I would have to predict the behaviour of the joystick operator.

So, maybe not the right way of looking at the problem, but I need to be shown why this is poor thinking not just that it IS poor thinking.

"The set speed is useful ONLY when you tell the stepper to step a bunch of times, and then call the run() method to allow the stepper to decide if it is time to step."

Of all the things in your post, this is the most interesting. When I look at the Arduino documentation page for stepper.h (https://www.arduino.cc/en/reference/stepper) I don't see anything talking about this. Nada, zilch. PLEASE tell me where I can read actual INSTRUCTIONS on how to use stepper.h and it's functions!

If you just ask the Stepper library to make a single step it cannot take account of any speed setting. You have to figure out the interval between steps yourself.

And I am NOT saying that one-step-at-a-time is wrong. There may be very good reason to do that.

This is not sensible IMHO

  static int XnegStart= (analogRead(A0)-10);
  static int XposStart = (analogRead(A0)+10);

because you might get two very different readings. This would be better

  static int XnegStart= (analogRead(A0)-10);
  static int XposStart = XnegStart + 20);

...R

"If you just ask the Stepper library to make a single step it cannot take account of any speed setting."

Sending StepperY.step(2) fixed it.

If it true then that:

The StepperY.setSpeed(VAL) when used by the stepper.h instance is actually representing a time delay BETWEEN TWO FULL CYCLES on the two coils of the stepper motor.

Meaning coil 1 is energised, then coil 2 is energised, then the delay to the next full cycle is what is controlled by the setSpeed() value.

Like this:
Coil1ON/Coil1OFF/Coil2ON/Coil2Off/DELAYVALUE/Coil1ON/Coil1OFF/Coil2ON/Coil2Off/DELAYVALUE/Coil1ON/Coil1OFF...

That would seem to explain why one step won't work.

If it was
Coil1ON/DELAYVALUE/Coil1OFF/DELAYVALUE/Coil2ON/DELAYVALUE/Coil2Off/DELAYVALUE/Coil1ON/...

Then my one-step method would work, I think.

If you can, please let me know if I am getting at the right thinking here?
?

So you set the speed and take 2 steps at that speed. Good. Now how much time is it to the 3rd step? You have to control that too.

The reference to run() is in the AccelStepper library. That is probably a better library for this project.

"2 analogRead()s inefficient". Yes but that is not the real problem. The real problem is inconsistent data. If you read once to decide when to move and then again to find how much to move, the analog value will change between those two readings. The "much" may find it doesn't need to move when it was just told to start moving.

Holy everloving hell.

AccelStepper.

Rewriting the whole thing now. Wow. Everything I want to do is bult right into that library. And go figure, it has has real, meaningful, and informative documentation!!!

Thank you, MorganS!

I had seen the name of the library but being new to arduino and having seen so much that is written for specific brands of add-on hardware I assumed that it was for a specific board/controller module and had not gone to read it's page. So cool to now know better!