Sabertooth 2x25 Simpified Serial Mode ( Tank Style

I have a sabertooth 2x25 and I want it to control my 2pcs 24 volt scooter motors.

I found this working code used in a balancing segway http://sites.google.com/site/onewheeledselfbalancing/Home/easy-build-low-cost-arduino-self-balancer

I tweaked the code for the balancing segway to fit my need, but it had an error in compiling..HELP

My application: Control ( forward, reverse) 2 pcs of scooter motor upon keyboard press by using the Arduino and sabertooth motor controller

My code:

#include <SoftwareSerial.h>

/*****************************************************

  • DIP Switches as per the wized:
    • NiMh Battery ( Our Case: Lead Acid)
    • TTL RS232
    • Simplified Serial Mode
    • Just one Sabertooth conected
    • 9600 baudrate
  • Pin 1 - ON
  • Pin 2 - OFF
  • Pin 3 - ON
  • Pin 4 - OFF
  • Pin 5 - ON
  • Pin 6 - ON
    ****************************************************/

// Labels for use with the Sabertooth 2x5 motor controller

// Digital pin 13 is the serial transmit pin to the
// Sabertooth 2x5
#define SABER_TX_PIN 13

// NOT USED (but still init'd)
// Digital pin 12 is the serial receive pin from the
// Sabertooth 2x5
#define SABER_RX_PIN 12

// Set to 9600 through Sabertooth dip switches
#define SABER_BAUDRATE 9600

// Simplified serial Limits for each motor
#define SABER_MOTOR1_FULL_FORWARD 127
#define SABER_MOTOR1_FULL_REVERSE 1
#define SABER_MOTOR1_FULL_STOP 64

#define SABER_MOTOR2_FULL_FORWARD 255
#define SABER_MOTOR2_FULL_REVERSE 128
#define SABER_MOTOR2_FULL_STOP 192

// Motor level to send when issuing the full stop command
#define SABER_ALL_STOP 0

SoftwareSerial SaberSerial = SoftwareSerial( SABER_RX_PIN,
SABER_TX_PIN );

void initSabertooth( void )
{
// Init software UART to communicate
// with the Sabertooth 2x5
pinMode( SABER_TX_PIN, OUTPUT );

SaberSerial.begin( SABER_BAUDRATE );

// 2 second time delay for the Sabertooth to init
delay( 2000 );

// Send full stop command
setEngineSpeed( SABER_ALL_STOP );
}

/*****************************************************

  • setEngineSpeed
  • Inputs - cSpeed_Motor1 - Input a percentage of full
  • speed, from -100 to +100

*****************************************************/
void setEngineSpeed( signed char cNewMotorSpeed )
{
initSabertooth( );
unsigned char cSpeedVal_Motor1 = 0;

unsigned char cSpeedVal_Motor2 = 0;

// Check for full stop command
if( cNewMotorSpeed == 0 )
{
// Send full stop command for both motors
SaberSerial.print( 0, BYTE );

return;
}

// Calculate the speed value for motor 1
if( cNewMotorSpeed >= 100 )
{
cSpeedVal_Motor1 = SABER_MOTOR1_FULL_FORWARD;

cSpeedVal_Motor2 = SABER_MOTOR2_FULL_FORWARD;
}
else if( cNewMotorSpeed <= -100 )
{
cSpeedVal_Motor1 = SABER_MOTOR1_FULL_REVERSE;

cSpeedVal_Motor2 = SABER_MOTOR2_FULL_REVERSE;
}
else
{
// Calc motor 1 speed (Final value ranges from 1 to 127)
cSpeedVal_Motor1 = map( cNewMotorSpeed,
-100,
100,
SABER_MOTOR1_FULL_REVERSE,
SABER_MOTOR1_FULL_FORWARD );

// Calc motor 2 speed (Final value ranges from 128 to 255)
cSpeedVal_Motor2 = map( cNewMotorSpeed,
-100,
100,
SABER_MOTOR2_FULL_REVERSE,
SABER_MOTOR2_FULL_FORWARD );
}

// Fire the values off to the Sabertooth motor controller
SaberSerial.print( cSpeedVal_Motor1, BYTE );

SaberSerial.print( cSpeedVal_Motor2, BYTE );
}

void move( )
{
initSabertooth( );
}

void control ( )
{
// Full stop
setEngineSpeed( 0 );

// Half reverse
setEngineSpeed( -50 );

// Full reverse
setEngineSpeed( -100 );

// Half forward
setEngineSpeed( 50 );

// Full forward
setEngineSpeed( 100 );
}

DIFFERENTIAL STEERING: Motor 1: Left side Motor 2: Right side

void setEngineSpeedDir( signed char cNewMotorSpeedDir )
{
unsigned char cSpeedValDir_Motor1 = 0;

unsigned char cSpeedValDir_Motor2 = 0;

if( cNewMotorSpeedDir >= 100 )
{
cSpeedValDir_Motor1 = SABER_MOTOR1_FULL_FORWARD; // GO RIGHT

cSpeedValDir_Motor2 = SABER_MOTOR2_FULL_STOP;
}

else if( cNewMotorSpeedDir <= -100 )
{
cSpeedValDir_Motor1 = SABER_MOTOR1_FULL_STOP; // GO LEFT

cSpeedValDir_Motor2 = SABER_MOTOR2_FULL_FORWARD;
}

else
{
// Calc motor 1 speed (Final value ranges from 64 to 127)
cSpeedValDir_Motor1 = map( cNewMotorSpeedDir,
-100,
100,
SABER_MOTOR1_FULL_STOP,
SABER_MOTOR1_FULL_FORWARD );

// Calc motor 2 speed (Final value ranges from 192 to 255)
cSpeedValDir_Motor2 = map( cNewMotorSpeedDir,
-100,
100,
SABER_MOTOR2_FULL_FORWARD,
SABER_MOTOR2_FULL_STOP);
}

// Fire the values off to the Sabertooth motor controller
SaberSerial.print( cSpeedValDir_Motor1, BYTE );

SaberSerial.print( cSpeedValDir_Motor2, BYTE );

void moveit( )
{
initSabertooth( );
}

void turn ( )
{
// Turn left
setEngineSpeedDir( -100 );

// Turn right
setEngineSpeedDir( 100 );

}

//KEY PRESS: (10/12/10)

void key() {

If(Serial.available()>0) {

Int data = Serial.read();
digitalWrite(buttonPin,LOW);
switch(data)
{
case'w':traverse = 100;break; //full forward
case's':traverse = -50;break; // half reverse
case'q':traverse = 0;break; // Stop
case'a':negotiate = -100;break; // left
case'd':negotiate = 100;break; // right
}
setEngineSpeed( traverse );
setEngineSpeedDir( negotiate );
}

{

These issues jump out at me.

DIFFERENTIAL STEERING: Motor 1: Left side             Motor 2: Right side

Comments need to have // in front

If(Serial.available()>0) {

Int data = Serial.read();

You might look for If and Int here:

Hint: you'd be more likely to find if and int...

I tweaked the code for the balancing segway to fit my need, but it had an error in compiling..HELP

What was the "error in compiling"? Is "tweaked" another way of saying "screwed up"?

Hi Paul thanks for the correction, but another compiling error occured:

a function definition is not allowed here { before token

sketch_oct14a.cpp: In function 'void setEngineSpeedDir(signed char)':
sketch_oct14a:208: error: a function-definition is not allowed here before '{' token
sketch_oct14a:213: error: a function-definition is not allowed here before '{' token
sketch_oct14a:231: error: a function-definition is not allowed here before '{' token
sketch_oct14a:250: error: expected `}' at end of input

I'm also confused on where to put the void setup () and loop () and how to call the functions I made, not sure if my whole code would work fine :frowning:

Tweaked means edit for my case :slight_smile: ...

Help..
Regards

If you used consistent and proper indenting, you'd see that you are missing a closing brace for the setEngineSpeedDir function.

I'm also confused on where to put the void setup () and loop () and how to call the functions I made, not sure if my whole code would work fine

The position of the setup() and loop() functions relative to the other functions does not matter.

You must have had some reason for creating the functions. That is "this function takes these arguments and does this...". That should make it easy to know how to call the functions. When you need something done, call the function that does it, and pass it the values that it needs to do it's thing.

When the code compiles and uploads, you'll know it the code works, by the way the hardware performs (or know that it doesn't, by the way the hardware doesn't perform, or performs incorrectly).

Tweaked means edit for my case

I was just tweaking you... :sunglasses:

Here My edited setEngineSpeedDir function and Key press:

//DIFFERENTIAL STEERING: Motor 1: Left side Motor 2: Right side

void setEngineSpeedDir( signed char cNewMotorSpeedDir )
{
unsigned char cSpeedValDir_Motor1 = 0;

unsigned char cSpeedValDir_Motor2 = 0;

if( cNewMotorSpeedDir >= 100 )
{
cSpeedValDir_Motor1 = SABER_MOTOR1_FULL_FORWARD; // GO RIGHT

cSpeedValDir_Motor2 = SABER_MOTOR2_FULL_STOP;
}

else if( cNewMotorSpeedDir <= -100 )
{
cSpeedValDir_Motor1 = SABER_MOTOR1_FULL_STOP; // GO LEFT

cSpeedValDir_Motor2 = SABER_MOTOR2_FULL_FORWARD;
}

}

else
{
// Calc motor 1 speed (Final value ranges from 64 to 127)
cSpeedValDir_Motor1 = map( cNewMotorSpeedDir,
-100,
100,
SABER_MOTOR1_FULL_STOP,
SABER_MOTOR1_FULL_FORWARD );

// Calc motor 2 speed (Final value ranges from 192 to 255)
cSpeedValDir_Motor2 = map( cNewMotorSpeedDir,
-100,
100,
SABER_MOTOR2_FULL_FORWARD,
SABER_MOTOR2_FULL_STOP);
}

// Fire the values off to the Sabertooth motor controller
SaberSerial.print( cSpeedValDir_Motor1, BYTE );

SaberSerial.print( cSpeedValDir_Motor2, BYTE );

void moveit( )
{
initSabertooth( );
}

void turn ( )
{
// Turn left
setEngineSpeedDir( -100 );

// Turn right
setEngineSpeedDir( 100 );

}

//KEY PRESS: (10/12/10)

void key()
{

signed char traverse;
signed char negotiate;
int buttonPin

if(Serial.available()>0) {

int data = Serial.read();
digitalWrite(buttonPin,LOW);
switch(data)
{
case'w':traverse = 100;break; //full forward
case's':traverse = -50;break; // half reverse
case'q':traverse = 0;break; // Stop
case'a':negotiate = -100;break; // left
case'd':negotiate = 100;break; // right
}
setEngineSpeed( traverse );
setEngineSpeedDir( negotiate );
}

{

But still with error: :frowning:

expected unqualified-id before 'else'

ketch_oct14a:181: error: expected unqualified-id before 'else'
sketch_oct14a:199: error: expected constructor, destructor, or type conversion before '.' token
sketch_oct14a:201: error: expected constructor, destructor, or type conversion before '.' token
sketch_oct14a.cpp: In function 'void key()':
sketch_oct14a:238: error: expected initializer before 'if'
sketch_oct14a:255: error: expected `}' at end of input

You have any clue?

Stripping out the code that actually is in the write place, and does something, you have this:

void setEngineSpeedDir( signed char cNewMotorSpeedDir )
{
   if( cNewMotorSpeedDir >= 100 )
   {
   }
   else if( cNewMotorSpeedDir <= -100 )
   {
   }
}
[glow]else
{
}

// Fire the values off to the Sabertooth motor controller
SaberSerial.print( cSpeedValDir_Motor1, BYTE );

SaberSerial.print( cSpeedValDir_Motor2, BYTE );[/glow]

Look at the indenting I applied, and look at the highlighted code. It is that code, that is now clearly in the wrong place, that is causing all the errors.

Paul,

I have no clue why this part of my code is not working.. It is patterned with the code above which had no error.. Is it possible to combine them? Or any way to restructure it?

Clueless... :-[

You have a function that starts with the statement

void setEngineSpeedDir( signed char cNewMotorSpeedDir )

The body of the function starts with a { and ends with a }. In your case, the body contains an if statement, with a body enclosed by { and }, and an else if statement, with a body enclosed by { and }.

The function is followed by an else statement. The compiler is telling you that the else must follow an if statement, not a function.

No code can be present outside of a function body, with the exception of global variable declarations and assignments (and these only when the the assignment is part of the declaration).

The closing brace for your function is in the wrong place. Move it after the else statement body and serial print statements.

Properly indenting code, and lining up the opening and closing braces on their own lines makes problems like this much easier to spot. That's why I consistently harp on this subject.

Thanks Paul!

Finally got my code working, but it has yet to be tested with my sabertooth although it already compiled :slight_smile:

Everything works fine, but when I add my loop function it errors:
I want to loop my setEngineSpeed( signed char cNewMotorSpeed ) and setEngineSpeedDir( signed char cNewMotorSpeedDir ); functions, but to no avail :frowning:

Any ideas?

sketch_oct15a.cpp: In function 'void loop()':
sketch_oct15a:250: error: expected primary-expression before 'signed'
sketch_oct15a:252: error: expected primary-expression before 'signed'

void loop ()
{

setEngineSpeed( signed char cNewMotorSpeed );

setEngineSpeedDir( signed char cNewMotorSpeedDir );
control ();
turn ();

}

Regards

I was looking at your setEngineSpeedDir function. I think it needs some work.

There are two things that you need to tell the motor - which direction to spin, and how fast. Speed and direction.

A variable name like cSpeedValDir_Motor1 is confusing. Is that the variable that controls speed or the one that controls direction?

How does the type of the input argument to that function, signed char, convey either speed or direction, let alone both?

Now, to address the questions you most recently asked.

Look at how you call map() in the setEngineSpeedDir function. The map function takes 5 arguments, so it's definition looks like:

long map(long x, long in_min, long in_max, long out_min, long out_max)

It is called like this:

val = map(val, 0, 1023, 0, 255);

Now, look at the loop function you have. You have defined a function (like map) called setEngineSpeedDir that has a definition that looks like:

void setEngineSpeedDir( signed char cNewMotorSpeedDir )

But, you are trying to call it like this:

 setEngineSpeed( signed char cNewMotorSpeed );

You need to pass a value to the function, either explicitly

setEngineSpeedDir('A');

or implicitly
char way = 'T';

setEngineSpeedDir(way);

When you try to figure out what values to call the function with, and you are struggling like you are, there is a good chance that you don't understand what the function does or you don't know anything about programming.

Fess up. Which is it?

First of all my application is tank style steering so: turning right entails turning the right motor off and running the left motor forward and vice versa for turning left.

mode of operation in sabertooth: simplified serial mode

according to the datasheet:

Simplified serial uses TTL level single-byte serial commands to set the motor speed and direction. This makes it easy to interface to microcontrollers and PCs, without having to implement a packet-based communications protocol. Simplified serial is a one-direction only interface. The transmit line from the host is connected to S1. The host's receive line is not connected to the Sabertooth. Because of this, multiple drivers can be connected to the same serial transmitter. If using a true RS-232 device like a PC's serial port, it is necessary to use a level converter to shift the –10V to 10V rs-232 levels to the 0v-5v TTL levels the Sabertooth is expecting. This is usually done with a Max232 type chip. If using a TTL serial device like a microcontroller, the TX line of the microcontroller may be connected directly to S1.

Because Sabertooth controls two motors with one 8 byte character, when operating in Simplified Serial mode, each motor has 7 bits of resolution. Sending a character between 1 and 127 will control motor 1. 1 is full reverse, 64 is stop and 127 is full forward. Sending a character between 128 and 255 will control motor 2. 128 is full reverse, 192 is stop and 255 is full forward.
Character 0 (hex 0x00) is a special case. Sending this character will shut down both motors.

Yes, cSpeedValDir_Motor1 controls the movement of motor 1
The datasheet says it accepts characters, so I used the chars to control both motors( see part of the datasheet above).

So I will have to pass a value to the function so as to call it properly..like:

A = signed char cNewMotorSpeed

then I will call it by: setEngineSpeedDir('A');

is that fine?

I admit that I'm not good at programing, but I want to learn though :-/

So I will have to pass a value to the function so as to call it properly..like:

A = signed char cNewMotorSpeed

then I will call it by: setEngineSpeedDir('A');

is that fine?

No, for several reasons.

First, a char variable can hold 8 bits. This allows one of two ranges. Either -127 to 127 or 0 to 255. If the first range is desired, the variable should be a signed char. If the 2nd range is desired (as it is in your case), the variable should be an unsigned char.

Second, that is not the correct way to define a variable. Try this:
unsigned char cNewMotorSpeed = 'A';

Third, there is no reason to have a variable if you are passing an explicit value to the function.

Finally, do you have any idea which way which motor will spin, or how fast, if you pass the function 'A'?

You can pass the function a numeric value, as long as the value is in the range 0 to 255.

So, you would really want to call the function like this:

setEngineSpeedDir(47);

Finally, your comment:

First of all my application is tank style steering so: turning right entails turning the right motor off and running the left motor forward and vice versa for turning left.

Much smoother control of the steering, and much tighter turns, can be accomplished by turning both treads at the same time. Stopping one to accomplish a turn is possible, but slowing one while speeding up the other, will also accomplish a turn, although with a larger radius. Turning one track forward while turning the other track in reverse will also cause a turn, with a much smaller radius.

Thanks Paul, I'll try this and update you

Hi Paul,

After troubleshooting I finally got it to work, but with no keypress code.. It wont respond to it.. I need it to be cotrolled by my: WSADQ keys.. Any clue?

My working code ( no keypress):

#include <SoftwareSerial.h>

// Labels for use with the Sabertooth 2x5 motor controller

// Digital pin 13 is the serial transmit pin to the
// Sabertooth 2x5
#define SABER_TX_PIN 13

// NOT USED (but still init'd)
// Digital pin 12 is the serial receive pin from the
// Sabertooth 2x5
#define SABER_RX_PIN 12

// Set to 9600 through Sabertooth dip switches
#define SABER_BAUDRATE 9600

// Simplified serial Limits for each motor
#define SABER_MOTOR1_FULL_FORWARD 127
#define SABER_MOTOR1_FULL_REVERSE 1
#define SABER_MOTOR1_FULL_STOP 64

#define SABER_MOTOR2_FULL_FORWARD 255
#define SABER_MOTOR2_FULL_REVERSE 128
#define SABER_MOTOR2_FULL_STOP 192

// Motor level to send when issuing the full stop command
#define SABER_ALL_STOP 0

SoftwareSerial SaberSerial = SoftwareSerial( SABER_RX_PIN, SABER_TX_PIN );

void initSabertooth( void )
{
// Init software UART to communicate
// with the Sabertooth 2x5
pinMode( SABER_TX_PIN, OUTPUT );

SaberSerial.begin( SABER_BAUDRATE );

// 2 second time delay for the Sabertooth to init
delay( 2000 );
// Send full stop command
setEngineSpeed( SABER_ALL_STOP );
}

/************************************************** ***

  • setEngineSpeed
  • Inputs - cSpeed_Motor1 - Input a percentage of full
  • speed, from -100 to +100

************************************************** ***/
void setEngineSpeed( signed char cNewMotorSpeed )
{
unsigned char cSpeedVal_Motor1 = 0;

unsigned char cSpeedVal_Motor2 = 0;

// Check for full stop command
if( cNewMotorSpeed == 0 )
{
// Send full stop command for both motors
SaberSerial.print( 0, BYTE );

return;
}

// Calculate the speed value for motor 1
if( cNewMotorSpeed >= 100 )
{
cSpeedVal_Motor1 = SABER_MOTOR1_FULL_FORWARD;

cSpeedVal_Motor2 = SABER_MOTOR2_FULL_FORWARD;
}
else if( cNewMotorSpeed <= -100 )
{
cSpeedVal_Motor1 = SABER_MOTOR1_FULL_REVERSE;

cSpeedVal_Motor2 = SABER_MOTOR2_FULL_REVERSE;
}
else
{
// Calc motor 1 speed (Final value ranges from 1 to 127)
cSpeedVal_Motor1 = map( cNewMotorSpeed,
-100,
100,
SABER_MOTOR1_FULL_REVERSE,
SABER_MOTOR1_FULL_FORWARD );

// Calc motor 2 speed (Final value ranges from 128 to 255)
cSpeedVal_Motor2 = map( cNewMotorSpeed,
-100,
100,
SABER_MOTOR2_FULL_REVERSE,
SABER_MOTOR2_FULL_FORWARD );
}

// Fire the values off to the Sabertooth motor controller
SaberSerial.print( cSpeedVal_Motor1, BYTE );
SaberSerial.print( cSpeedVal_Motor2, BYTE );
}

void setup( )
{
initSabertooth( );
}

//void control( )
//{
// // Full stop
// setEngineSpeed( 0 );
//
// // Half reverse
// setEngineSpeed( -50 );
//
// // Full reverse
// setEngineSpeed( -100 );
//
// // Half forward
// setEngineSpeed( 50 );
//
// // Full forward
// setEngineSpeed( 100 );
//}

void setEngineSpeedDir( signed char cNewMotorSpeedDir )
{
unsigned char cSpeedValDir_Motor1 = 0;

unsigned char cSpeedValDir_Motor2 = 0;

if( cNewMotorSpeedDir >= 100 )
{
cSpeedValDir_Motor1 = SABER_MOTOR1_FULL_FORWARD; // GO RIGHT

cSpeedValDir_Motor2 = SABER_MOTOR2_FULL_STOP;
}
else if( cNewMotorSpeedDir <= -100 )
{
cSpeedValDir_Motor1 = SABER_MOTOR1_FULL_STOP; // GO LEFT

cSpeedValDir_Motor2 = SABER_MOTOR2_FULL_FORWARD;
}
else
{
// Calc motor 1 speed (Final value ranges from 64 to 127)
cSpeedValDir_Motor1 = map( cNewMotorSpeedDir,
-100,
100,
SABER_MOTOR1_FULL_STOP,
SABER_MOTOR1_FULL_FORWARD );

// Calc motor 2 speed (Final value ranges from 192 to 255)
cSpeedValDir_Motor2 = map( cNewMotorSpeedDir,
-100,
100,
SABER_MOTOR2_FULL_FORWARD,
SABER_MOTOR2_FULL_STOP);
}

// Fire the values off to the Sabertooth motor controller
SaberSerial.print( cSpeedValDir_Motor1, BYTE );

SaberSerial.print( cSpeedValDir_Motor2, BYTE );
}

//void turn ( )
//{
// // Turn left
// setEngineSpeedDir( -100 );
//
// // Turn right
// setEngineSpeedDir( 100 );
//}

void loop()
{
setEngineSpeedDir( -100 );
delay( 5000 );
setEngineSpeed( 0 );

// signed char traverse;
// signed char negotiate;
//
// if(Serial.available()>0)
// {
// int data = Serial.read();
//
//// digitalWrite(buttonPin,LOW);
//
// switch(data)
// {
// case'w':traverse = 100;break; //full forward
// case's':traverse = -50;break; // half reverse
// case'q':traverse = 0;break; // Stop
// case'a':negotiate = -100;break; // left
// case'd':negotiate = 100;break; // right
// }
//
// setEngineSpeed( traverse );
// setEngineSpeedDir( negotiate );
// }
}

After troubleshooting I finally got it to work, but with no keypress code.. It wont respond to it.. I need it to be cotrolled by my: WSADQ keys.. Any clue?

Do you mean beyond the obvious that all the code to read and handle serial data is commented out?

What application on the PC is listening to, and sending to the serial port, the key presses?

Hey Paul,

I commented out the keypress part because it does not respond to the keys when pressed, so I tried to assign the specific chars to the motor and it worked fine.

My setup is: 2 motors, batteries, sabertooth ( motor controller), and the arduino.. so I want it to respond to the keyboard press on my computer, but it won't..I don't know why.

any ideas?

Regards

Do you have an application running on the PC that is capturing the key presses and sending them to the serial port? If so, what does that code look like.

If not, you need one.

My only application is arduino 019 compiler/loader.. What applications captures the keypresses( using laptop keyboard)?

What applications captures the keypresses?

You need to create one. There are a variety of ways to go about this, depending on your skill- and tool-set. Visual Studio (Windows only; free versions are available from microsoft.com) allows you to create C++ or C# applications that can capture keystrokes, and send them to the serial port. If you send them to the same one that the Arduino is connected to, the Arduino can read the characters sent.

Processing is another possibility. It includes several examples that send and receive serial data, and it includes examples that get keyboard input. Combining two sketches is pretty simple.

Hi Paul,

I'm quite interested with the Microsoft Visual Studio that creates C++ or C# applications that can capture keystrokes, and send them to the serial port....

I'm just not sure how to set this up both software and hardware so that it can communicate with my arduino. Do you have samples?

For the Processing, I found this code: http://wiki.processing.org/w/W-A-S-D_control_keyboard_input ,but how can I incorporate it with my arduino? for the control?

Regards