Smooth joystick real time control

1,954 views
Skip to first unread message

Barnaby Coote

unread,
Aug 1, 2016, 12:03:21 PM8/1/16
to accelstepper
Hi all. I have been using this library for a few days and the motion for it provides fro pre-determined moves is really top notch. I'm now trying to get it to work smoothly with a joystick control so i can move the motor around and set reference points manually. I am currently reading a square value from the analog joystick and putting that in setSpeed() which works well but can result in jerky motion if I push the joystick 100% one way quickly. With something heavy this could cause skipped steps.

What I would like is to have the motor gradually ramp up to the joystick's speed value and ramp down when I let it go back to zero (or to any new speed the joystick is moved to). I have tried smoothing out my joystick values but the motion isn't nearly as good as the s-cuvres AccelStepper provides. Is there a way I can implement a function do to this using the library ? Has anyone managed somehow ? 

Seems like a very useful scenario and having looked through the library it seems like it should be possible, though there are some things in the math that I still don't understand enough to write new parts. Can give me some insight into this ?

Mike McCauley

unread,
Aug 1, 2016, 4:53:13 PM8/1/16
to accels...@googlegroups.com
Hello,

AccelStepper was not designed for this use case. Over to you.
--
Mike McCauley VK4AMM mi...@airspayce.com
Airspayce Pty Ltd 9 Bulbul Place Currumbin Waters QLD 4223 Australia
http://www.airspayce.com
Phone +61 7 5598-7474

Mike McCauley

unread,
Aug 1, 2016, 7:54:22 PM8/1/16
to accels...@googlegroups.com
Hi,

On Tuesday, August 02, 2016 06:53:07 AM Mike McCauley wrote:
> Hello,
>
> AccelStepper was not designed for this use case. Over to you.

That said, try this:

// Joystick.pde
// -*- mode: C++ -*-
//
// Track forward/reverse speed from a joystick
//
// Copyright (C) 2016 Mike McCauley
// $Id: $

#include <AccelStepper.h>

// Define a stepper and the pins it will use
AccelStepper stepper; // Defaults to AccelStepper::FULL4WIRE (4 pins) on 2, 3,
4, 5

#define JOYSTICK_PIN A0
#define MAX_SPEED 100
#define INPUT_READ_INTERVAL 100
unsigned long last_input_time = 0;

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

// Change these to suit your stepper if you want
stepper.setMaxSpeed(0);
stepper.setAcceleration(10);
// stepper.moveTo(1000000000);
}

void loop()
{
// Every INPUT_READ_INTERVAL milliseconds, read inputs.
// We do this infrequently to prevent interfering
// with the stepper motor high speed stepping
// Get the current joystick position as analog value

unsigned long current_time = millis();
if (current_time - last_input_time > INPUT_READ_INTERVAL)
{
int joystick_in = analogRead(JOYSTICK_PIN);
// Map the raw analog value to speed range from -MAX_SPEED to MAX_SPEED
int desired_speed = map(joystick_in, 0, 1023, -MAX_SPEED, MAX_SPEED);
// Serial.println(desired_speed);

// Based on the input, set targets and max speed
stepper.setMaxSpeed(abs(desired_speed));
if (desired_speed == 0 && stepper.speed() == 0)
{
// Prevent running off the end of the position range
stepper.setCurrentPosition(0);
}
else if (desired_speed < 0)
{
stepper.moveTo(-1000000000);
}
else if (desired_speed > 0)
{
stepper.moveTo(1000000000);
}
last_input_time = current_time;
}


stepper.run();

Bob Roszkowski

unread,
Jul 19, 2017, 2:18:17 AM7/19/17
to accelstepper
Hi Mike, W5EVH here, this code is great!  Exactly what I was looking for.  Do you know if there is a way to setup a "dead" zone in the middle of the joystick?  Mine seems to be not quite on center.

Thanks mate!

boB

Mike McCauley

unread,
Jul 19, 2017, 2:33:34 AM7/19/17
to accels...@googlegroups.com

Hi,

 

You might try changing the speed tests:

 

else if (desired_speed < 0) 

 

to, say

 

else if (desired_speed < -10) 

 

and so on.

 

I havent tested this so YMMV.

 

Cheers.

gregor

unread,
Jul 19, 2017, 2:55:32 AM7/19/17
to accelstepper
hi,

my joystick code:


int16_t
Joystick::getAxisValue(joystick_axis_t axis)
{
    uint8_t axis_pin
;
    int16_t offset
;

   
if (axis == X_AXIS)
   
{
        axis_pin
= x_axis_pin_;
        offset
= x_offset_;
   
}

   
else //if (axis == Y_AXIS)
   
{
        axis_pin
= y_axis_pin_;
        offset
= y_offset_;
   
}

   
//read current joystick axis position.
    int16_t raw_value
= analogRead(axis_pin);

   
//apply offset.
    raw_value
-= offset;

   
//shift the range from 0 - 1023 to -512 to 512.
    int16_t axis_value
= map(raw_value, 0, 1023, -MAX_VALUE, MAX_VALUE);

   
//constrain values to -512 to 512 range.
    axis_value
= constrain(axis_value, -MAX_VALUE, MAX_VALUE);

   
//apply deadzone.
   
if (axis_value > -deadzone_ / 2 && axis_value < deadzone_ / 2)
       
return 0;

   
return axis_value;
}
where offset is the difference between the ideal and real center position of this axis (measured when creating the Joystick object) and deadzone_ is the deadzone of the joystick (in AREF/ADC_Resolution Volts)

Bob Roszkowski

unread,
Jul 20, 2017, 7:22:49 PM7/20/17
to accelstepper
Hi Gregor,

Im not sure how to implement that in the above code.

Bob Roszkowski

unread,
Jul 21, 2017, 3:17:40 PM7/21/17
to accelstepper
Does this code need the joystick library in order to work?

gregor

unread,
Jul 21, 2017, 5:00:27 PM7/21/17
to accelstepper
this code is a snippet of my own joystick class. 

implemented in the code above, my approach looks like this:
void loop() 
  // Every INPUT_READ_INTERVAL milliseconds, read inputs. 
  // We do this infrequently to prevent interfering 
  // with the stepper motor high speed stepping 
  // Get the current joystick position as analog value 

  unsigned long current_time = millis(); 
  if (current_time - last_input_time > INPUT_READ_INTERVAL) 
  { 
    int joystick_in = analogRead(JOYSTICK_PIN); 

    //shift the value from [0,1023] range to [-512, 512] range
    joystick_in = map(joystick_in, 0, 1023, -512, 512);

    //deadzone
    if (abs(joystick_in) < 10)
      joystick_in = 0;
    
    // Map the raw analog value to speed range from -MAX_SPEED to MAX_SPEED 
    int desired_speed = map(joystick_in, -512, 512, -MAX_SPEED, MAX_SPEED); 
//    Serial.println(desired_speed); 

    // Based on the input, set targets and max speed 
    stepper.setMaxSpeed(abs(desired_speed)); 
    if (desired_speed == 0 && stepper.speed() == 0) 
    { 
      // Prevent running off the end of the position range 
        stepper.setCurrentPosition(0); 
    } 
    else if (desired_speed < 0) 
    { 
      stepper.moveTo(-1000000000); 
    } 
    else if (desired_speed > 0) 
    { 
      stepper.moveTo(1000000000); 
    } 
    last_input_time = current_time; 
  } 


  stepper.run(); 
changes in bold.

Bob Roszkowski

unread,
Jul 22, 2017, 2:59:35 AM7/22/17
to accelstepper
@gregor
Thank you so much for your help.  I really appreciate you taking the time.  This works perfect for my implementation.

gregor christandl

unread,
Jul 22, 2017, 3:09:08 AM7/22/17
to accelstepper

You're welcome!


--
You received this message because you are subscribed to the Google Groups "accelstepper" group.
To unsubscribe from this group and stop receiving emails from it, send an email to accelstepper...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Timeijs

unread,
Oct 18, 2018, 4:39:31 PM10/18/18
to accelstepper
Hi, I know this is probably too late for you guys, but for future readers:

For me, Mikem's approach to achieve a "dead spot" resulted in the arduino slowly moving into one direction when the joystick was in de dead spot.I have no idea why this happened, but I solved it by adding the following right after the if-statement:

else {stepper.stop();}

The resulting full if-statement then being as follows: (the values 20 and -20 worked well for my joystick)

 if (desired_speed == 0 && stepper.speed() == 0) 
    { 
      // Prevent running off the end of the position range 
        stepper.setCurrentPosition(0); 
    } 
    else if (desired_speed < -20) 
    { 
      stepper.moveTo(-1000000000); 
    } 
    else if (desired_speed > 20) 
    { 
      stepper.moveTo(1000000000); 
    }
    else {stepper.stop();} 

I hope this might help anyone who had the same problem as I had in the future! This is my first time on this forum, so if I did anything wrong please let me know...

kondordt83

unread,
Nov 11, 2022, 11:13:56 AM11/11/22
to accelstepper
Hello and welcome. I got to this post because I have been trying for a long time to understand the Accelstepper library (I am not a programmer) and apply it to move two stepper motors that will be controlled independently by joystick X, Y - They will guide the cart on v-slot profiles. For now, I have limited myself to one stepper motor to learn the principle of operation. The above code will work great for me, but I would like to add limit buttons to it. I understand that the basis of this program is the continuous reading of the joystick value through the millis () function in the loop, but if I add the limiting buttons, their value must also be read constantly in the millis () loop so that the carriage stops when the limiting button is pressed and he moved back a few steps in the opposite direction, for the moment deactivating the code responsible for moving the wheelchair with the joystick. So the loop with the limiting buttons must have priority over the one that controls X, Y. I wonder where to put the loop of limit buttons in this code.

kondordt83

unread,
Nov 11, 2022, 11:15:52 AM11/11/22
to accelstepper
I mean this code:

}

Jim Larson

unread,
Nov 15, 2022, 8:27:16 PM11/15/22
to accelstepper
Just to understand a little better, why do you need limit switches/stop pushbuttons if you are controlling the speed and direction with a joystick??

kondordt83

unread,
Nov 16, 2022, 4:37:38 AM11/16/22
to accelstepper
Hi. I assumed in advance that you could lose attention, so I want to protect the gantry from colliding at the ends.
For now, I'm trying like this:

#define JOYSTICK_PIN 34
#define MAX_SPEED 2000
#define INPUT_READ_INTERVAL 100

#define Limit_1 27
#define Limit_2 33


unsigned long last_input_time = 0;

#include <AccelStepper.h>

AccelStepper stepper (1, 14, 12); // ESP32

void setup ()
{
  Serial.begin (9600);
  pinMode (Limit_1, INPUT_PULLUP);
  pinMode (Limit_2, INPUT_PULLUP);


  // Change these to suit your stepper if you want
  stepper.setMaxSpeed ​​(0);
  stepper.setAcceleration (6000);
  // stepper.moveTo (1000000000);
}

void loop ()

{
  // Every INPUT_READ_INTERVAL milliseconds, read inputs.
  // We do this infrequently to prevent interfering
  // with the stepper motor high speed stepping
  // Get the current joystick position as analog value

  unsigned long current_time = millis ();
  if (current_time - last_input_time> INPUT_READ_INTERVAL)
  {
    int joystick_in = analogRead (JOYSTICK_PIN);
    // shift the value from [0,1023] range to [-512, 512] range
    joystick_in = map (joystick_in, 0, 4095, -512, 512);
    //Serial.println(joystick_in);

    // deadzone
    if (abs (joystick_in) <200)
      joystick_in = 0;


    // Map the raw analog value to speed range from -MAX_SPEED to MAX_SPEED
    int desired_speed = map (joystick_in, -512, 512, -MAX_SPEED, MAX_SPEED);
    //Serial.println(desired_speed);


    // Based on the input, set targets and max speed
    stepper.setMaxSpeed ​​(abs (desired_speed));
    if (desired_speed == 0 && stepper.speed () == 0)

    {
      // Prevent running off the end of the position range
      stepper.setCurrentPosition (0);
    }
    else if (desired_speed <0 && (digitalRead (Limit_1)))
    {
      stepper.moveTo (-1000000000);
    }
    else if (desired_speed> 0 && (digitalRead (Limit_2)))
    {
      stepper.moveTo (1000000000);
    }
    else {
      stepper.stop ();
      {
        if (desired_speed <0)
          stepper.setSpeed ​​(0);
        if (desired_speed> 0)
          stepper.setSpeed ​​(0);
      }
    }
    last_input_time = current_time;
  }
  stepper.run ();
}

But I don't know how to do to control both X and Y axes independently in this one code.
Reply all
Reply to author
Forward
0 new messages