Controlling gear motors with Arduino and joystick.

Hello everyone. I'm working on a Heavy duty remote(wired) control spotlight. Similar to a www.golight.com except not rubbish junk. I have the other components worked out except the stepper motors/and control system. Here is the plan. 1. Mount the spotlight http://www.surefire.com/HeavyGunWeaponLight on an exclosed aluminum platform. 2. Control the spotlight with 2 stepper motors and arduino controllers. 3.Use Slip rings so i can traverse continously. 4. Simple joystick to control direction/elevation, and toggle switchs to turn the system on and off and the light on and off.

I think this project would be would best using large stepper motors with lots of torque, that way when you release the joystick it will hold its postion especilly going over rought terrain. http://cgi.ebay.com/Nema-42-Stepper-Motor-4120oz-in-1-Driver-8A-CNC-Mill-/200578908511?pt=LH_DefaultDomain_0&hash=item2eb36f415f simular to this for example.

I would also like to control the motors with http://www.makershed.com/ProductDetails.asp?ProductCode=MKPX10 or similar.

I have found a wealth of information on controlling these large stepper motors with a computer; also lots of informaton on controlling small servo motors with a joystick and an arduino board. But nothing on exactly what I want to do. Is it even possible? I would like to know if Arduino is an answer before I jump in. Thanks

Nick

Thanks for the reply,

Yes this will be vehicle mounted 8) A member of this project has some great fab skills so we will be able to seal this up.

As far as the slip rings, we won't be happy unless this can traverse continously, elevation only needs to be about 90 degrees so we will only need 1 slip ring.

Right now i am concerned that the stepper motors will stand up to vibration of offroading. Thats another reason I was looking at larger ones.

I'm looking at the Uno board, would that work to control the servo motor drivers, or would I be better off using Motor shield with External switches? Please forgive my ignorance. Thanks

Yes the elevation motor's electrical will have to go threw the slip ring as well as power to the spotlight.

The reason I want to go with steppers is for variable speed and precision and the massive holding torque, but yes looks like gear motors would be much easier to deal with. What fun would that be? Here is a VERY crude drawing of what im planning, 5 min in MS PAINT!

Another problem im seeing is that all of the larger stepper motor drivers are powed by 110V AC so i would need a dedicated DC powersupply.

Right now im looking at interfacing the Arduino Uno Board with A Probotix Breakout Board http://www.probotix.com/index.php?view=product&product_id=16

I should be able to connect the Uno with the Parallel port on the Breakout Board. Will it work?

Sorry that was worded wrong, I need a 24V DC\DC powersupply as my power source is 12V DC. Because most of the (power supplies) that come with thse kits work off AC. Yes, all the drivers ive seen work off 24-80VDC.

On the motor. The base, vertical shaft and traverse motor are stationary. The traverse motor will turn a large gear that is attached to the housing.

This is not the case with the elevation motor, Where the gear is attached to the shaft and the motor is attached to the housing.

This will alow MOST of the electrical components to be inside ONE housing. Not a very good explanation, and a poor drawing. But It will work with just one slip ring. I have the mechanical all figured out, just not sure about the whole communication chain.

Thanks

Nema Stepper Motors are Monsters xD

I like these: http://cgi.ebay.com/New-12V-DC-30-RPM-High-Torque-Gear-Box-Electric-Motor-/320733379575?pt=LH_DefaultDomain_0&hash=item4aad32fbf7

http://cgi.ebay.com/12V-130RPM-Torque-Gear-Box-Motor-New-/220765757966?pt=LH_DefaultDomain_0&hash=item3366aa220e

They are light, easy to use, and cheaper (not only motor, but the driver too). Course, nema42 has 10 times more torque, but you really need that much torque?

Once Again thanks for the help!

As per everyone's advice (and the massive cost savings) I went with some smaller DC gear motors. They are 130rpm(have to gear them down a bit) and draw up to about 1A at stall. (they are also difficult to turn when they are not energized!) The current plan is to hook up 4 motors in parallel (the megamoto is rated for 15-30amps so I should be good)

I recieved my Ardunio UNO, Megamoto shield, and joystick. This Thing is really cool! Anyway Ive started to play with it, and hooked up the joystick. The Ardunio can read it's position with the Analog Serial Read!

(Fuzzy phone picture)

Anyway I'm having a hard time understanding the programming when there isn't a tutorial. I was able to find a library on the megamoto, but its much more complex than what I need.

Can someone point me in the direction of a library that would work? Also, would something like the diagram work good for limit switches in my application?

Thanks!

Thanks for the quick relpy KE7GKP, guess I need to be more specific!

Here is the only library I found http://code.google.com/p/megamoto-superservo/ What I need to do is simple (If I could program) control the speed and direction of the motors using a joystick.

On the limit switch diagram replace the replays with the megamoto board.

As far as the motors went, Yes i figured they would be (close enough) since they will all be turning the same load. Like placing the motors 90degrees apart turning a central gear. Am I totally wrong here? Thanks

You could use two toggle switchs like below to control the motor directions. As to joysticks, there are joysticks which can have the sping return to center removed/defeated for use with servos.

Switches won't let me control motor speed.

The code works! But not real well, The motor can be controlled forward and backward, but I cant seem to find an Off position. (either forward or reverse) I think it's my joystick. Last week it seem to read nice and sharp and had the whole range of values. Today its hardly working. Mayby I'm doing something wrong. It had values from 0-254 and a center postion of 127. Now its 0-866 with a center position of 56.

It was cheap enough, can anyone recomend me a good 10k 2 axis joystick?

This is the unit im using. http://www.parallax.com/Store/Microcontrollers/BASICStampModules/tabid/134/txtSearch/27800/List/1/ProductID/581/Default.aspx?SortField=ProductName%2cProductName

For vehicle use im looking at this. http://search.digikey.com/scripts/DkSearch/dksus.dll?Detail&name=4R112S1E3500-ND

Thanks everyone

SWEET! Thanks for the link!

10K? Because I don't fully understand.

Need some help with the code, what can I remove?
The code works, just needs to be cleaned up some and tweaked.
I have the board up and running but can’t seem to find a stopped position with the joystick. I must be doing the mapping wrong.

This is the original code, the only bit I have played with is rc0scaledmap (adjusting it with analog serial read) any suggestions for this newb?
I have read the tutorial on mapping but got nothing out of it.
Thanks

int EnablePin = 8;         // MegaMoto: Enable Controller
int PWMPin = 11;           // MegaMoto: Timer2 
int PWMPin2 = 3;           // MegaMoto: 
const byte CPin = 0;       // MegaMoto: analog input channel
int CRaw;                  // MegaMoto: raw A/D value
float CVal;                // MegaMoto: adjusted Amps value
float KP = 25;             // PID: position multiplier (gain) was 2 Adjust to get more aggressive or conservative control 
float KI = 8;              // PID: Intergral multiplier (gain) was .05 set about the same as your manual response time Seconds/4 
float KD = 0;              // PID: Derivative multiplier (gain) http://www.expertune.com/tutor.html
int lastError = 0;         // PID: track the previous error for the derivitive term, and the sum of the errors for the integral term
int sumError = 0;          // PID: 
int iMax = 100;            // PID: Integral term min/max (random value and not yet tested/verified)
int iMin = 0;              // PID: 
int potPin = 4;            // PID: the servo POT
unsigned long rc0=0;       // RC input variable 
unsigned long rc0scaled=0; // RC input scaled with map function
int dutyMax = 254;         // For testing reduce this to a low number and do not set it higher than 254

void setup() {   
  pinMode(EnablePin, OUTPUT);  // MegaMoto:   
  pinMode(PWMPin, OUTPUT);     // MegaMoto:
  pinMode(PWMPin2, OUTPUT);    // MegaMoto:
  setPwmFrequency(PWMPin, 8);  // MegaMoto: change Timer2 divisor to 8 gives 3.9kHz PWM freq
  Serial.begin(57600);
  pinMode (5, INPUT);          // RC input pin PWM 5 on Arduino connected to signal pin on RC receiver also connect grounds
  digitalWrite(EnablePin, HIGH);
  delay(500);
  digitalWrite(EnablePin, LOW);// Reset overcurrent or overtemp fault.  May need to let the MegaMoto cool.
  delay(500);
  digitalWrite(EnablePin, HIGH);
}

void loop() {
  int val = analogRead(potPin);   // read the potentiometer that provides the position feedback for the servo (0 - 1023)
  checkTransmitter();             // check the data being received by the transmitter.  Sets the value for rc0
  Serial.print (rc0);
  Serial.print ("   ");
  rc0scaled = map(rc0, 91, 201, 0, 1023);  // rc0 min and max are entered here from observing serial output and moving radio stick
  int error = val - rc0scaled;    // PID: find the error term of current position val minus the target rc scaled input
  Serial.print ("   ");
  Serial.print (rc0scaled);
                                  // PID: generalized PID formula
                                  // PID: correction = Kp * error + Kd * (error - prevError) + kI * (sum of errors)
  int ms = KP * error + KD * (error - lastError) + KI * (sumError); // PID: calculate a motor speed for the current conditions
  // PID: set the last and sum errors for next loop iteration
  lastError = error;
  sumError += error;
  // PID: scale the sum for the integral term
  if(sumError > iMax){
    sumError = iMax;
  }
  else if(sumError < iMin){
    sumError = iMin;
  }
  // PID: determine the direction since the MegaMoto expects posotive values from 0 to 254
  digitalWrite(EnablePin, HIGH);                  // keep enabled  
  if(ms > 0){                                     // BACKWARD DIRECTION   This code block is duplicated (except for the PWMPin assignments) below for Forward Direction 
    int motorSpeed = map(ms,0,2047,0,254);
    if(motorSpeed > dutyMax){                     // make sure the max of 254 is never exceeded.    
      motorSpeed = dutyMax;
    }
    if(motorSpeed < 20){                          // stop repositioning "chattering" the servo when at rest 
      motorSpeed = 0;                             // If the motorSpeed lessthan amount is changed here change it below as well.   
    }
    PWMPin = 11;                                  // swap pins to change direction
    PWMPin2 = 3;
    // CRaw = analogRead(CPin);                   // this reads amps from MegaMoto
    analogWrite(PWMPin2, 0);
    analogWrite(PWMPin, motorSpeed);
    delay(50);
    Serial.print ("  R ");
    Serial.println (motorSpeed);
  }
  if(ms < 0){                                     // FORWARD DIRECTION
  ms = -1 * ms;
    int motorSpeed = map(ms,0,2047,0,254);
    if(motorSpeed > dutyMax){                     // make sure the max of 254 is never exceeded. 
      motorSpeed = dutyMax;
    }
    if(motorSpeed < 20){                          // stop repositioning "chattering" the servo when at rest
      motorSpeed = 0;
    }
    PWMPin = 3;                                   //  Pins are swapped to change direction relative to the above BACKWARD code block
    PWMPin2 = 11;
    // CRaw = analogRead(CPin);                   // this reads amps from MegaMoto
    analogWrite(PWMPin2, 0);
    analogWrite(PWMPin, motorSpeed);
    delay(50);
    Serial.print ("  F ");
    Serial.println (motorSpeed);
  }
}

// RC input
void checkTransmitter() 
{
rc0 = (pulseIn (5, HIGH, 100000))/10; // read RC channel
} 

//  MegaMotor: 
//  Since pwm 5 is used for RC input, portions of this routine are commented out.  pwm 6 would be good input of 2nd rc channel.
void setPwmFrequency(int pin, int divisor) {
  byte mode;
  if(pin == 5 || pin == 6 || pin == 9 || pin == 10) { // Timer0 or Timer1
    switch(divisor) {
      case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 64: mode = 0x03; break;
      case 256: mode = 0x04; break;
      case 1024: mode = 0x05; break;
      default: return;
    }
    if(pin == 5 || pin == 6) {                                    // the commented out lines are because pwm pin 5 is for RC input pwm pin 6 would be 2nd rc channel
      // TCCR0B = TCCR0B & 0b11111000 | mode; // Timer0
    } else {
      // TCCR1B = TCCR1B & 0b11111000 | mode; // Timer1
    }
  } else if(pin == 3 || pin == 11) {
    switch(divisor) {
      case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 32: mode = 0x03; break;
      case 64: mode = 0x04; break;
      case 128: mode = 0x05; break;
      case 256: mode = 0x06; break;
      case 1024: mode = 0x7; break;
      default: return;
    }
    TCCR2B = TCCR2B & 0b11111000 | mode; // Timer2
  }
}

Need some help with the code, what can I remove? The code works, just needs to be cleaned up some and tweaked. I have the board up and running but can't seem to find a stopped position with the joystick. I must be doing the mapping wrong.

I'd suggest you drop back from the code you copied and just work on getting a single pot and servo to properly read, map, and position.

OK finally made some progress.

I was able to find a much more simple library.

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1295133308

I “borrowed” the code and removed alot of stuff I didn’t need.
Here it is!

//
//
// Tank bot work in progress 30/12/10 version 0.5:


#include <Servo.h>



//turret and servo(s)

Servo scanservo ;
Servo Larmservo ;
Servo Harmservo ;


//motor pins and reverse

const int RTMPin     = 11 ;
const int RTRPin     = 3 ;
const int LTMPin     = 9 ;
const int LTRPin     = 10 ;

// pointometers/joystick

int potpin   = A3 ;
int joyx     = A4 ;
int joyy     = A5 ;

// variables
int potval  = 0 ;
int joyxval = 0 ;
int joyyval = 0 ;
int RTval   = 0 ;
int LTval   = 0 ;
int LDval   = 0 ;
int RDval   = 0 ;

/////////////////////////////////////////////////////////////////////////////////////

void setup() {

Serial.begin(9600);

pinMode (RTMPin, OUTPUT);
pinMode (RTRPin, OUTPUT);
pinMode (LTMPin, OUTPUT);
pinMode (LTRPin, OUTPUT);

pinMode (potpin, INPUT);
pinMode (joyx,   INPUT);
pinMode (joyy,   INPUT);
}

/////////////////////////////////////////////////////////////////////////////////////

void loop()
{
  potval = analogRead(potpin);
  LDval = analogRead(joyy);
  RDval = analogRead(joyy);
  LDval = map (LDval, 525, 1023, 0, 255);
  RDval = map (RDval, 500, 0, 0, 255);


if (analogRead(joyx)<512)
{
LTval = analogRead(joyx);
LTval = map(LTval, 500, 0, 0, 255);
RTval = analogRead(joyx);
RTval = map(RTval, 500, 0, 0, 255);


LTval = (LTval - constrain (RDval, 0, 255));
RTval = (RTval - constrain (LDval, 0, 255));

analogWrite  (RTMPin, constrain (LTval, 0, 255));
analogWrite  (LTMPin, constrain (RTval, 0, 255));
digitalWrite (RTRPin, HIGH);
digitalWrite (LTRPin, HIGH);
}
if (analogRead(joyx)>513)
{
LTval = analogRead(joyx);
LTval = map(LTval, 525, 1023, 0, 255);
RTval = analogRead(joyx);
RTval = map(RTval, 525, 1023, 0, 255);


LTval = (LTval - constrain (RDval, 0, 255));
RTval = (RTval - constrain (LDval, 0, 255));

analogWrite  (RTMPin, constrain (LTval, 0, 255));
analogWrite  (LTMPin, constrain (RTval, 0, 255));
digitalWrite (RTRPin, LOW);
digitalWrite (LTRPin, LOW);
}

delay(150);

}

It’s working great with the joystick value 525-1023
But when the joystick value is 0-500 the PWM is opposite what it should be.

So on the lower half of the joystick 0 should be full reverse and 500 should be minimum reverse.
And on the upper half 525 should be minimum speed and 1023 should be full speed.

The upper half is working great, just the lower half is inverted, Full reverse is 500.

Must have something wrong in my code, Any suggestions? So close to getting this running!
Thanks,
Nick

Servos use PPM modulation and not PWM, so using a servo library may get you better results. Below is some servo test code for use with the serial monitor that can be be modified to map 0-1023 to 0-180 for testing.

// zoomkat 10-4-10 serial servo test
// type servo position 0 to 180 in serial monitor
// for writeMicroseconds, use a value like 1500
// for IDE 0019 and later
// Powering a servo from the arduino usually DOES NOT WORK.

String readString;
#include <Servo.h>
Servo myservo; // create servo object to control a servo

void setup() {
Serial.begin(9600);
myservo.writeMicroseconds(2000); //set initial servo position if desired
myservo.attach(7); //the pin for the servo control
Serial.println(“servo-test-21”); // so I can keep track of what is loaded
}

void loop() {

while (Serial.available()) {
delay(1);
if (Serial.available() >0) {
char c = Serial.read(); //gets one byte from serial buffer
readString += c; //makes the string readString
}
}

if (readString.length() >0) {
Serial.println(readString); //so you can see the captured string
int n;
char carray[6]; //converting string to number
readString.toCharArray(carray, sizeof(carray));
n = atoi(carray);
myservo.writeMicroseconds(n); // for microseconds
//myservo.write(n); //for degees 0-180
readString="";
}
}

Made some great progress, It now works!!!
I just have a few deadspace issues, I have tried entering some code but it dosen’t seem to work.
Cleaned it up some.

//
//
// Tank bot work in progress 30/12/10 version 0.5:


#include <Servo.h>



//turret and servo(s)

Servo scanservo ;
Servo Larmservo ;
Servo Harmservo ;


//motor pins and reverse

const int RTMPin     = 11 ;
const int RTRPin     = 3 ;

// pointometers/joystick

int potpin   = A3 ;
int joyx     = A4 ;

// variables
int potval  = 0 ;
int joyxval = 0 ;
int RTval   = 0 ;
int LTval   = 0 ;
int LDval   = 0 ;
int RDval   = 0 ;

/////////////////////////////////////////////////////////////////////////////////////

void setup() {

Serial.begin(9600);

pinMode (RTMPin, OUTPUT);
pinMode (RTRPin, OUTPUT);
pinMode (joyx,   INPUT);
}

/////////////////////////////////////////////////////////////////////////////////////

void loop()
{
  potval = analogRead(potpin);
  LDval = analogRead(joyx);
  RDval = analogRead(joyx);
  LDval = map (LDval, 530, 1023, 0, 255);
  RDval = map (RDval, 500, 0, 0, 255);


if (analogRead(joyx)<500)
{
LTval = analogRead(joyx);
LTval = map(LTval, 0, 500, 0, 255);
RTval = analogRead(joyx);
RTval = map(RTval, 0, 500, 0, 255);


LTval = (LTval - constrain (RDval, 0, 255));
RTval = (RTval - constrain (LDval, 0, 255));

analogWrite  (RTMPin, constrain (LTval, 0, 255));
digitalWrite (RTRPin, HIGH);
}
if (analogRead(joyx)>530)
{
LTval = analogRead(joyx);
LTval = map(LTval, 530, 1023, 0, 255);
RTval = analogRead(joyx);
RTval = map(RTval, 530, 1023, 0, 255);


LTval = (LTval - constrain (RDval, 0, 255));
RTval = (RTval - constrain (LDval, 0, 255));

analogWrite  (RTMPin, constrain (LTval, 0, 255));
digitalWrite (RTRPin, LOW);
}

delay(150);

}

Just need to resolve the joystick deadspace issues and it should be perfect.

Hya. This has been a great read.

Wondered if you got this project up and running ? Also what schematic were you working from if any ?

My project is really similar. 2 motors joystick and a pots to change speed

One motor for pan and one for tilt.

Would this idea be adaptable ?

You don’t need such convoluted coding to get PWM from the joystick reading,
just use the abs() function to get a drive level:

  pwm = constrain (abs (joystick - 512) / 2, 0, 255) ;

for instance - this returns the positive difference from 512 scaled down to 0…255 all
in one line. You can detect a deadband by a subsequent test on the size of pwm:

  if (pwm < DEADBAND)
    pwm = 0 ;