My new rover is supposed to be simpler than the first one and something that irked me with the first design was the motor controllers. They worked, but they are ridiculously complicated. So I bought a Sparkfun Monster Moto Controller and hooked it up - much simpler.
The only hassles with it is that if you would plug another board on top of it, then it could short to the tops of the capacitors and the VIN connector could short to the Arduino ICS pins underneath it. I stuck a rectangle of clear plastic cut from some screws packaging between the boards and snipped the ICS pins off - done.
Here is an example for a serial motor control interface, using simple two character messages:
// Monster Moto Board
// Herman Oosthuysen, Sep 2016
// Monster motor controller with simple two character ASCII serial interface
// Controls two motors to make a rover run forward reverse and turn
// Turns are executed by speeding up the motor on one side and slowing down (or reversing) on other side
// A command starts with M and ends with Enter (CR or LF)
// MS - Stop, short the motors to ground
// MF - Forward, best to slow down and stop before going backward
// MB - Backward, best to slow down and stop before going forward
// ML - Turn Left, cancel a left turn with a right turn
// MR - Turn Right, cancel a right turn with a left turn
// MU - Speed Up, only three speed steps, stop, slow and fast
// MD - Slow Down, only three speed steps, stop, slow and fast
// Literals
#define BAUD 9600
#define RATE 200
#define CR 0x0D
#define LF 0x0A
#define MAX 2
#define MIN -2
#define INC 0x20
#define MAXCS 0x80
// Pins
// Motor 1
#define INA1 7
#define INB1 8
#define PWM1 5
#define EN1 A0
#define CS1 A2
// Motor 2
#define INA2 4
#define INB2 9
#define PWM2 6
#define EN2 A1
#define CS2 A3
// Global Variables
int cs1 = 0; // 0 to FF
int cs2 = 0; // 0 to FF
int spd1 = 0; // -2 to 2
int spd2 = 0; // -2 to 2
int pwm1 = 0; // 0 to 1023
int pwm2 = 0; // 0 to 1023
char ch = 0; // ASCII character received
char adr = 0; // ASCII M start of Motor message
char cmd = 0; // ASCII command
void setup()
{
Serial.begin(BAUD);
Serial.println("Monster Moto, eh.");
// Stop the motors
pinMode(INA1, OUTPUT);
digitalWrite(INA1, LOW);
pinMode(INA2, OUTPUT);
digitalWrite(INA2, LOW);
pinMode(INB1, OUTPUT);
digitalWrite(INB1, LOW);
pinMode(INB2, OUTPUT);
digitalWrite(INB2, LOW);
// PWM zero speed
analogWrite(PWM1, 0);
analogWrite(PWM2, 0);
}
void loop()
{
if(Serial.available())
{
ch = Serial.read();
// Message starts with M and ends with Enter
// eg: ms[enter]
// Ensure that the serial terminal sends the line ends
if((ch == CR) | (ch == LF))
{
adr = 0;
cmd = 0;
}
else if((ch == 'M') | (ch == 'm'))
{
adr = 1;
cmd = 'M';
}
else if(adr)
{
cmd = ch;
Serial.print("cmd = ");
switch(cmd)
{
case 'S':
case 's':
spd1 = 0;
spd2 = 0;
pwm1 = 0;
pwm2 = 0;
Serial.println(cmd);
break;
case 'F':
case 'f':
spd1 = 1;
spd2 = 1;
pwm1 = spd1 * INC;
pwm2 = pwm1;
Serial.println(cmd);
break;
case 'B':
case 'b':
spd1 = -1;
spd2 = -1;
pwm1 = abs(spd1) * INC;
pwm2 = pwm1;
Serial.println(cmd);
break;
case 'L':
case 'l':
spd1--;
if(spd1 < MIN)
spd1 = MIN;
spd2++;
if(spd2 > MAX)
spd2 = MAX;
pwm1 = abs(spd1) * INC;
pwm2 = abs(spd2) * INC;
Serial.println(cmd);
break;
case 'R':
case 'r':
spd1++;
if(spd1 > MAX)
spd1 = MAX;
spd2--;
if(spd2 < MIN)
spd2 = MIN;
pwm1 = abs(spd1) * INC;
pwm2 = abs(spd2) * INC;
Serial.println(cmd);
break;
case 'U':
case 'u':
spd1++;
if(spd1 > MAX)
spd1 = MAX;
spd2++;
if(spd2 > MAX)
spd2 = MAX;
pwm1 = abs(spd1) * INC;
pwm2 = abs(spd2) * INC;
Serial.println(cmd);
break;
case 'D':
case 'd':
spd1--;
if(spd1 < MIN)
spd1 = MIN;
spd2--;
if(spd2 < MIN)
spd2 = MIN;
pwm1 = abs(spd1) * INC;
pwm2 = abs(spd2) * INC;
Serial.println(cmd);
break;
default:
Serial.println();
Serial.print("Err = ");
Serial.println(cmd);
break;
}
Serial.print("spd1 = ");
Serial.println(spd1);
Serial.print("spd2 = ");
Serial.println(spd2);
Serial.print("pwm1 = ");
Serial.println(pwm1);
Serial.print("pwm2 = ");
Serial.println(pwm2);
}
}
// Periodic Motor Control Update
sense();
direction(spd1, spd2);
speed(pwm1, pwm2);
delay(RATE);
}
// Left and right hand motors rotate in opposite directions
void direction(int spd1, int spd2)
{
if(spd1 >= 0)
{
digitalWrite(INA1, HIGH); // CW - Forward
digitalWrite(INB1, LOW);
}
else
{
digitalWrite(INA1, LOW); // CCW - Reverse
digitalWrite(INB1, HIGH);
}
if(spd2 >= 0)
{
digitalWrite(INA2, LOW); // CCW - Forward
digitalWrite(INB2, HIGH);
}
else
{
digitalWrite(INA2, HIGH); // CW - Reverse
digitalWrite(INB2, LOW);
}
}
void speed(int pwm1, int pwm2)
{
analogWrite(PWM1, pwm1);
analogWrite(PWM2, pwm2);
}
void sense(void)
{
cs1 = analogRead(CS1);
cs2 = analogRead(CS2);
if((cs1 > MAXCS) | (cs2 > MAXCS))
{
spd1 = 0;
spd2 = 0;
}
}
PWM motor control is not linear. The motors need a minimum amount of power to start turning and after that, the speed increases rapidly until a maximum is reached. So the performance curve is S shaped and some experiments are required with your motors if you want to have meaningful speed steps for crawl, walk and run for example.
A high powered motor controller should be close to the motor because of the high currents in the wires. So in a big system, you may need one Arduino per motor controller. If you use the same serial interface protocol on multiple Arduino powered actuators, then you can multi-drop the serial line from the main control computer to the various actuator computers. For this example, the messages start with M and for something else, it could start with S or whatever else you like.
For a simple toy, error checks are a waste of time, so I just send a message and hope it works. I don't bother with CRCs and Retries on toys, but if you want to control a winch or a wheel chair, then you should be more careful!
BTW, if you need to build something serious operating at 12V, then I recommend that you get Anti-Gravity batteries. These are light weight and will ensure that one can still manhandle the thing - lead-acid batteries make it impossible to lift a wheel-chair into a car.
La Voila!
Herman
The only hassles with it is that if you would plug another board on top of it, then it could short to the tops of the capacitors and the VIN connector could short to the Arduino ICS pins underneath it. I stuck a rectangle of clear plastic cut from some screws packaging between the boards and snipped the ICS pins off - done.
Serial Control
Controlling a DC motor is straight forward, using two pins to switch the H bridge direction (INA1, INB1) and one for speed PWM (PWM1). There is also a current sense input (CS1) that you can set to turn the motors off if they get stuck and the current increases too much. You'll have to set the sense level with trial and terror.Here is an example for a serial motor control interface, using simple two character messages:
- ms - stop
- mf - forward
- mb - backward
- mr - turn right
- ml - turn left
- mu - speed up
- md - slow down
// Monster Moto Board
// Herman Oosthuysen, Sep 2016
// Monster motor controller with simple two character ASCII serial interface
// Controls two motors to make a rover run forward reverse and turn
// Turns are executed by speeding up the motor on one side and slowing down (or reversing) on other side
// A command starts with M and ends with Enter (CR or LF)
// MS - Stop, short the motors to ground
// MF - Forward, best to slow down and stop before going backward
// MB - Backward, best to slow down and stop before going forward
// ML - Turn Left, cancel a left turn with a right turn
// MR - Turn Right, cancel a right turn with a left turn
// MU - Speed Up, only three speed steps, stop, slow and fast
// MD - Slow Down, only three speed steps, stop, slow and fast
// Literals
#define BAUD 9600
#define RATE 200
#define CR 0x0D
#define LF 0x0A
#define MAX 2
#define MIN -2
#define INC 0x20
#define MAXCS 0x80
// Pins
// Motor 1
#define INA1 7
#define INB1 8
#define PWM1 5
#define EN1 A0
#define CS1 A2
// Motor 2
#define INA2 4
#define INB2 9
#define PWM2 6
#define EN2 A1
#define CS2 A3
// Global Variables
int cs1 = 0; // 0 to FF
int cs2 = 0; // 0 to FF
int spd1 = 0; // -2 to 2
int spd2 = 0; // -2 to 2
int pwm1 = 0; // 0 to 1023
int pwm2 = 0; // 0 to 1023
char ch = 0; // ASCII character received
char adr = 0; // ASCII M start of Motor message
char cmd = 0; // ASCII command
void setup()
{
Serial.begin(BAUD);
Serial.println("Monster Moto, eh.");
// Stop the motors
pinMode(INA1, OUTPUT);
digitalWrite(INA1, LOW);
pinMode(INA2, OUTPUT);
digitalWrite(INA2, LOW);
pinMode(INB1, OUTPUT);
digitalWrite(INB1, LOW);
pinMode(INB2, OUTPUT);
digitalWrite(INB2, LOW);
// PWM zero speed
analogWrite(PWM1, 0);
analogWrite(PWM2, 0);
}
void loop()
{
if(Serial.available())
{
ch = Serial.read();
// Message starts with M and ends with Enter
// eg: ms[enter]
// Ensure that the serial terminal sends the line ends
if((ch == CR) | (ch == LF))
{
adr = 0;
cmd = 0;
}
else if((ch == 'M') | (ch == 'm'))
{
adr = 1;
cmd = 'M';
}
else if(adr)
{
cmd = ch;
Serial.print("cmd = ");
switch(cmd)
{
case 'S':
case 's':
spd1 = 0;
spd2 = 0;
pwm1 = 0;
pwm2 = 0;
Serial.println(cmd);
break;
case 'F':
case 'f':
spd1 = 1;
spd2 = 1;
pwm1 = spd1 * INC;
pwm2 = pwm1;
Serial.println(cmd);
break;
case 'B':
case 'b':
spd1 = -1;
spd2 = -1;
pwm1 = abs(spd1) * INC;
pwm2 = pwm1;
Serial.println(cmd);
break;
case 'L':
case 'l':
spd1--;
if(spd1 < MIN)
spd1 = MIN;
spd2++;
if(spd2 > MAX)
spd2 = MAX;
pwm1 = abs(spd1) * INC;
pwm2 = abs(spd2) * INC;
Serial.println(cmd);
break;
case 'R':
case 'r':
spd1++;
if(spd1 > MAX)
spd1 = MAX;
spd2--;
if(spd2 < MIN)
spd2 = MIN;
pwm1 = abs(spd1) * INC;
pwm2 = abs(spd2) * INC;
Serial.println(cmd);
break;
case 'U':
case 'u':
spd1++;
if(spd1 > MAX)
spd1 = MAX;
spd2++;
if(spd2 > MAX)
spd2 = MAX;
pwm1 = abs(spd1) * INC;
pwm2 = abs(spd2) * INC;
Serial.println(cmd);
break;
case 'D':
case 'd':
spd1--;
if(spd1 < MIN)
spd1 = MIN;
spd2--;
if(spd2 < MIN)
spd2 = MIN;
pwm1 = abs(spd1) * INC;
pwm2 = abs(spd2) * INC;
Serial.println(cmd);
break;
default:
Serial.println();
Serial.print("Err = ");
Serial.println(cmd);
break;
}
Serial.print("spd1 = ");
Serial.println(spd1);
Serial.print("spd2 = ");
Serial.println(spd2);
Serial.print("pwm1 = ");
Serial.println(pwm1);
Serial.print("pwm2 = ");
Serial.println(pwm2);
}
}
// Periodic Motor Control Update
sense();
direction(spd1, spd2);
speed(pwm1, pwm2);
delay(RATE);
}
// Left and right hand motors rotate in opposite directions
void direction(int spd1, int spd2)
{
if(spd1 >= 0)
{
digitalWrite(INA1, HIGH); // CW - Forward
digitalWrite(INB1, LOW);
}
else
{
digitalWrite(INA1, LOW); // CCW - Reverse
digitalWrite(INB1, HIGH);
}
if(spd2 >= 0)
{
digitalWrite(INA2, LOW); // CCW - Forward
digitalWrite(INB2, HIGH);
}
else
{
digitalWrite(INA2, HIGH); // CW - Reverse
digitalWrite(INB2, LOW);
}
}
void speed(int pwm1, int pwm2)
{
analogWrite(PWM1, pwm1);
analogWrite(PWM2, pwm2);
}
void sense(void)
{
cs1 = analogRead(CS1);
cs2 = analogRead(CS2);
if((cs1 > MAXCS) | (cs2 > MAXCS))
{
spd1 = 0;
spd2 = 0;
}
}
PWM motor control is not linear. The motors need a minimum amount of power to start turning and after that, the speed increases rapidly until a maximum is reached. So the performance curve is S shaped and some experiments are required with your motors if you want to have meaningful speed steps for crawl, walk and run for example.
Seriously High Power
MOSFETs can be paralleled, so if you need to control a very big motor, then you can wire the two channels together, so this is a really nice controller board that could control a winch, a scooter or a wheel chair for example. So it would be good if you need to build something to help a disabled friend - Sparkfun to the rescue!A high powered motor controller should be close to the motor because of the high currents in the wires. So in a big system, you may need one Arduino per motor controller. If you use the same serial interface protocol on multiple Arduino powered actuators, then you can multi-drop the serial line from the main control computer to the various actuator computers. For this example, the messages start with M and for something else, it could start with S or whatever else you like.
For a simple toy, error checks are a waste of time, so I just send a message and hope it works. I don't bother with CRCs and Retries on toys, but if you want to control a winch or a wheel chair, then you should be more careful!
BTW, if you need to build something serious operating at 12V, then I recommend that you get Anti-Gravity batteries. These are light weight and will ensure that one can still manhandle the thing - lead-acid batteries make it impossible to lift a wheel-chair into a car.
La Voila!
Herman
Comments
Post a Comment
On topic comments are welcome. Junk will be deleted.