After weeks of programming MegunoLink, I thought it was about time I built something. So I ordered a RedBot chassis and an Ardumotor driver from SparkFun. They have a neat demo that drives the thing back and forwards, but I want to drive it around. Maybe it can go fetch tasty treats while I’m busy debugging.
Choosing Commands to Control the RedBot
I figured the easiest place to start would be to make an Arduino program that let me control the robot using serial commands that I can send from MegunoLink. I used our serial command decoder to process the commands and started off with rather a long command list:
1 2 3 4 5 6 7 8 9 |
Commands.AddCommand(F("Fwd"), Cmd_Forward); Commands.AddCommand(F("Rev"), Cmd_Reverse); Commands.AddCommand(F("SpinLeft"), Cmd_SpinLeft); Commands.AddCommand(F("SpinRight"), Cmd_SpinRight); Commands.AddCommand(F("TurnLeft"), Cmd_TurnLeft); Commands.AddCommand(F("TurnRight"), Cmd_TurnRight); Commands.AddCommand(F("Stop"), Cmd_Stop); Commands.AddCommand(F("Right"), Cmd_MotorRight); Commands.AddCommand(F("Left"), Cmd_MotorLeft); |
When I started implementing all these, I realized they are all just a variation on setting the speed and direction for each of the two motors on the RedBot. Is going to use up a bunch of memory on the RedBot’s Arduino to have all these separate commands, not to mention take time to implement. So I dropped the command set to two: Move
and Stop
.
The Move
command takes two integer arguments, which provide the speed and direction for each motor. So !Move 128 -128\r\n
would rotate the left motor forwards and the right motor backwards at half-speed (maximum speed corresponds to 255). That will have the little RedBot spin on a spot — handy for getting around tight corners.
The Stop
command stops both motors. Strictly speaking I don’t need this one (we could just send !Move 0 0\r\n
), but when writing new programs and things are running amok, its handy to have a simple command that stops the chaos. I tested the stop command first.
Arduino code to Control the RedBot using Serial Commands
With just two serial commands to implement gives us a pretty simple Arduino program to control the RedBot. The serial command decoder does most of the work. I just register the commands it needs to process in the Arduino setup function and call its SerialCommandHandler.Process();
in the main loop. The handler will call the registered function whenever it sees a command that it knows about.
The Cmd_Move
function gets called for a move command. It retrieves the speed values from the command parameter using p.NextParameterAsInteger(0)
function. This function takes one argument: the default value to return if the parameter can’t be found on the command line. That would normally mean the user didn’t send a complete command. In that case we’d probably want the robot to stop, so I made the default value 0. Once it has the parameter values, Cmd_Move
updates the motor speed.
Cmd_Stop
is pretty simple. It just stops both motors by setting their speed to zero.
Here’s the RedBot Serial Controller listing:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
/* ===================================================================================== * RedBot Buggy Controller * * Arduino program to control a SparkFun RedBot using simple serial commands. Supported * commands are: * !Move LeftVelocity RightVelocity\r\n * Moves the robot using the velocity to determine speed and direction the motors * turn. Positive values move the robot forward, negative values move it backwards. * The maximum speed is 255. The motor is stopped when velocity is zero. * !Stop\r\n * Stops both motors. * * http://www.MegunoLink.com/articles/redbot-ui/ * ===================================================================================== */ #include "CommandHandler.h" // The serial command handler. Receives serial data and dispatches // recognised commands to functions registered during setup. // See: https://www.megunolink.com/documentation/arduino-libraries/serial-command-handler/ CommandHandler<> SerialCommandHandler; // Setup the pin assignments for the Ardumotor board. const int Pin_PWM_Right = 3; const int Pin_PWM_Left = 11; const int Pin_Direction_Right = 12; const int Pin_Direction_Left = 13; void setup() { Serial.begin(9600); Serial.println(F("Buggy Bot Controller")); Serial.println(F(__TIMESTAMP__)); Serial.println(F("--------------------")); // Initialize IO and make sure the robot is stopped. pinMode(Pin_PWM_Left, OUTPUT); pinMode(Pin_PWM_Right, OUTPUT); pinMode(Pin_Direction_Left, OUTPUT); pinMode(Pin_Direction_Right, OUTPUT); SetMotorSpeed(0,0); // Setup the command handler. SerialCommandHandler.AddCommand(F("Move"), Cmd_Move); SerialCommandHandler.AddCommand(F("Stop"), Cmd_AllStop); } void loop() { // Call the serial command handler's process function. It will receive // the serial data and call the registered function when a // recognized command is received. SerialCommandHandler.Process(); } /* --------------------------------------------------------------------------------- * Serial command handlers * --------------------------------------------------------------------------------- */ void Cmd_Move(CommandParameter &p) { int LeftSpeed = p.NextParameterAsInteger(0); int RightSpeed = p.NextParameterAsInteger(0); SetMotorSpeed(LeftSpeed, RightSpeed); } void Cmd_AllStop(CommandParameter &p) { SetMotorSpeed(0,0); } /* --------------------------------------------------------------------------------- * Motor control * --------------------------------------------------------------------------------- */ void SetMotorSpeed(int LeftSpeed, int RightSpeed) { // Print debug info. Serial.print(F("Speed: ")); Serial.print(LeftSpeed); Serial.print(' '); Serial.println(RightSpeed); // Update motor speed SetMotorSpeed(Pin_Direction_Left, Pin_PWM_Left, LeftSpeed); SetMotorSpeed(Pin_Direction_Right, Pin_PWM_Right, RightSpeed); } // Change the speed of one motor. DirectionPin and SpeedPins are the Arduino // pins that set direction and speed, respectively. If Speed is positive // the motor turns forwards; if negative it will spin backwards. void SetMotorSpeed(int DirectionPin, int SpeedPin, int Speed) { if (Speed > 0) { digitalWrite(DirectionPin, HIGH); } else { digitalWrite(DirectionPin, LOW); Speed = -Speed; } analogWrite(SpeedPin, Speed); } |
Building a RedBot UI with MegunoLink
I used MegunoLink’s interface panel visualizer to build a simple user interface to talk to the RedBot controller program. I could have just typed the commands into the Arduino serial monitor, or MegunoLink’s serial monitor window. But building an interface panel means I can have nice big buttons to click on and don’t have to remember the commands.
The interface is pretty simple. A number slider to control the speed and 7 big buttons to send the movement commands. You can download the MegunoLink project from GitHub.
I created the button images using CorelDraw, though I don’t recommend Corel anymore. Many people are having good success with Inkscape so I’ve included an SVG file, which Inkscape will open, in the repository.
Check out our guide to building an Arduino interface if you haven’t built an interface panel with MegunoLink before. Here are the commands that I assigned to each of the buttons:
Button | Command |
---|---|
TurnLeft | !Move [(int)Speed.Value/2] [Speed.Value]\r\n |
Forward | !Move [Speed.Value] [Speed.Value]\r\n |
TurnRight | !Move [Speed.Value] [(int)Speed.Value/2]\r\n |
SpinLeft | !Move [-Speed.Value] [Speed.Value]\r\n |
Stop | !Stop\r\n |
SpinRight | !Move [Speed.Value] [-Speed.Value]\r\n |
Reverse | !Move [-Speed.Value] [-Speed.Value]\r\n |
[Speed.Value]
picks up the value from the number slider (which is named Speed
) when the command is sent, letting me change the speed that the RedBot moves around.
It Moves!
But not at first.
The first problem I struck is that the RedBot wouldn’t run very well on carpet. Once I started running it on a piece of card it worked better, but was still quite sluggish.
After reading through the SparkFun site, I discovered they supply a 6V battery pack for good reason. The 5V you get from a USB port isn’t quite enough to excite the motors. Once I plugged in a 6V pack the little RedBot started zipping around.
Cool Trick
Click buttons on the interface panel worked well, but the bot was behind me. So I was watching the robot over my shoulder and trying to click on the buttons. Not super easy.
Windows supports accelerator keys for most interface elements, including buttons. They are the underlined characters on menus, buttons etc. The current design trend is to hide these things (presumably to make the interface less cluttered, but harder to use?!!), but they are still there.
And the way you setup accelerator keys is by putting a & character in the control text.
I didn’t really want text on my buttons (’cause they had pictures). But running the RedBot UI from the keyboard is pretty neat. So I setup the following accelerator keys on my buttons just by editing the button’s text in the interface panel designer:
Button | Button Text | Keyboard Shortcut |
---|---|---|
Forward | &W |
W |
SpinLeft | &A |
A |
Stop | &S |
S |
SpinRight | &D |
D |
Reverse | &X |
X |
In other-words, the keys used in some first-person shooter games. And now I can drive the little RedBot around using the W, A, S, D, X keys. Neat!
Debugging
Making just one command to control the RedBot motors worked well when it came to debugging. Once I got all the buttons doing something I realized the robot was moving in the wrong direction when I told it to turn left or right. Probably I had the motors around the wrong way. I didn’t make buttons to turn just one motor on but I can just send a command to turn one motor on using MegunoLink’s serial monitor visualizer:
And the wrong motor turned. Since the pin assignments are just constants at the top of the program, it was an easy matter to flip them around. Navigation is much easier when the motors are around the right way!