Introduction: Boom and Gripper Bot

Robotic grippers have always been fun things to play with. Adding one to a robot is like having a remote control Tonka toy from my childhood. In the past, grippers where a major deal to build and construct, but with 3d printers, CNC cutters and a wide variety of premade gripper kits, it makes it very easy to add one to your project now.

I wanted to make a simple bot with a gripper I could easily control from a computer or a phone app. It had to be wireless, agile and easy for me to make and change to get weight balancing under control. For this bot I used some of my Actobotics parts for the build. They made making the bot easy and I could make changes as the build progressed.

This bot uses the Actobotics channel and brackets for the main chassis, 6" ABS drive wheels (though 5" wheels would work just as well) a 2.9" skate wheel for the rear castor and an ABS gripper kit from ServoCity.

For the electronics; An uno clone for function control, a RoboClaw for the drive motors and a Serial servo controller from Pololu. A complete list of parts can be found in the next step.

Step 1: Hardware Bill of Materials

Electronics:

Motors:

  • 1x HiTech HS-625MG Ultra Speed (For the boom)
  • 1x HiTech HS-311 or similar (For the gripper)
  • 2x 116 RPM Precision Planetary Gear Motor (you can choose other RPMs or motors as well)

Chassis:

Wheels:

  • 4x 2.975 skate wheels (or just one if you counter weight the boom with something else)
  • 2x 6" ABS or 5" Acrylic
  • 2x Set screw hubs (545568)

Boom:

Batteries:

  • 1x 11.1v LI-io or Li-po and charger
  • 1x 6v battery pack for servos
  • 1x 5v USB power bank or 9V battery with 2.1mm wire connector for UNO

Misc:

  • 12 Female to Female jumper wires
  • 45x 6-32 3/16" screws
  • zip ties
  • double sided tape
  • 3x 1/4" standoffs
  • 1x 1.5" 6-32 screw and 2 nuts (to hold the wheel counter weights on with)

You can source many of the parts from eBay or other suppliers directly like Sparkfun, RobotShop and ServoCity.

After you have purchased, collected and downloaded everything thing you're ready to start with the build.

Step 2: The Software Needed

Things you will need to download:


  • The Arduino IDE
  • Python 2.7 (a full install with tkinter)
  • The pySerial library
  • The Robot code attached .zip (See the files in this Step)
  • The Python remote control application (See the files in this Step)
  • SoftwareSerial library

Step 3: Assembly - the Chassis

  1. Take two (2) of the 4.50" channels and place the one (1) 90° Quad Hub Mount C in each end.
  2. Now take the last 4.50" channel and place a 90° Dual Side Mount E on the bottom of the channel.

Take one of the 4.50" channels with the 90° Quad Hub Mount C on each end and one of the 9" channels

  • Place the 4.50" channel with the flat side up on the 4th hole of the 9" channel with the flat side down. Connect the two pieces together with 2 6-32 screws.
  • Now connect the other 9" channel in the same position on the opposite side.

Take the next 4.50" channels with the 90° Quad Hub Mount C on each end

  • Place the 4.50" channel with the flat side up on the 7th hole of the 9" channel with the flat side down. Connect the two pieces together with 2 6-32 screws.
  • Now connect the other 9" channel in the same position on the opposite side

Now take the last 4.50" with the 90° Quad Hub Mount C on each end

  • Connect the 90° Quad Hub Mount C to the first hole column on the 9" channel on both sides.

Next connect the two (2) 22mm motor mounts to the front of the chassis

See images for further details.

Assemble the Wheel bracket B per the ServoCity site info.

  • Place the swivel hub on top of the wheel bracket assembly and secure it with at least 2 6-32 screws
  • Connect the other side of the swivel hub to the 90° Single Angle Channel Bracket and secure with 4 6-32 screws.
  • Connect a 90° Quad Hub Mount C to the to the wheel assembly and the rear 4.50" channel of the chassis

See images for more details.

Step 4: Assembling the Gripper

Assemble the gripper following the instructions provided in this video. After finishing you should have something like the image.

Step 5: Assembling the Boom

For this step you will need the following:

  • 1x Servo block for HiTech1
  • 4" & 6" 5/8 tubing
  • 3x 5/8 bore tube clamp
  • 1x 90deg tube hub clamp
  • 1x Channel Bracket B
  • 1x 4.62” mini channel
  • 1x 3.85” Aluminum Beams
  • 1x 90° Quad Hub Mount C

NOTE: When I say the LEFT side of the bot I'm talking about the left side with the REAR of the bot facing you.

  • Take one 5/8 bore tube clamp and mount it on the first hole on the left side of the bot. Secure it with 4 screws from the bottom of the channel.
  • Next the 4" 5/8 tubing and place it in the clamp, tightening down the clamp screw.
  • Place another 5/8 clamp on the end of the 4" tube
  • Assemble the Servo Block per the ServoCity site info
  • Place the Servo Block onto the top of the clamping hub with the moro shaft facing right
  • Take the 4.62" mini channel and the 3.85" beam and bolt them together by two screws on the end. Set aside for now.
  • Next take the 6" tube and the 90deg tube clamp and place the clamp on one end of the tube. Mount the tube flush to the clamp end.
  • Now take the 6" tube assembly and place the 90deg clamp end on the servo hub on the servo block using two screws diagonally from each other.
  • Then mount the 4.62" mini channel and beam set between the two screws above and secure them on the 90deg clamp with two more screws.
  • Take the 90° Quad Hub Mount C and the channel bracket B and mount the hub to the open end of the channel bracket with 4 screws.
  • Next take the channel bracket B and hub C assembly and mount a 5/8 tube clamp on hub C end of the channel with 4 screws.
  • Place the clamping hub end of that part onto the end of the 6" tubing as pictured.
  • Now mount the gripper onto the channel bracket B and angle end.
  • Last, place the 3 skate wheels on the end of the boom for a counter weight. Use the 1.5" screw to hold them in-place with the two nuts as pictured.

SEE Images for more details

Step 6: Mounting the Electronics

Follow the pictures as a guide for mounting the electronic boards. Keep in mind the length of the jumper wires you have for maximum distances from each board.

Place the batteries and power bank near the board it will power in one of the side 9" channels.

Step 7: Wiring - the Motors

NOTE: When I say the LEFT side of the bot I'm talking about the left side with the REAR of the bot facing you.

  • Solder two wires to each motor long enough to reach the motor controller. I used black and red wires from here along with the connector board. You can choose other methods and or colors to your liking.
  • Run the wires you have for the LEFT and RIGHT sides through the chassis as pictured.
  • Connect the LEFT motor to the first output channel on the motor controller, with the BLACK wire in the first screw terminal and the RED in the second.
  • Connect the RIGHT motor to the second output channel on the motor controller, with the BLACK wire in the first screw terminal and the RED in the second.
  • Connect S1 to IO pin 12 of the UNO
  • Connect one of the GND pins of the RoboClaw to on on the UNO

SEE pictures for more details

NOTE: Later you may have to switch the RED and black wires around if the bot is not going in the correct direction once you power it on for testing and use.

Step 8: Wiring the Servos

Run the servo wires in a neat and tidy fashion to the Pololu servo controller. The gripper servo wire can be run through the 6" boom tube. You will need to use 3 jumper wires for it to be long enough to make it to the controller. For the boom servo wire, just wrap it around the 4" boom tube.

  • Boom servo -> Pololu servo controller servo '0'
  • Gripper servo -> Pololu servo controller servo '1'
  • Pololu servo controller GND pin to UNO GND pin
  • Pololu servo controller 5v to UNO 5v pin

See the pictures for more details.

Step 9: Wiring the Bluetooth Module

  • Bluetooth adaptor Tx -> UNO pin 0
  • Bluetooth adaptor Rx -> UNO pin 1
  • Bluetooth adaptor GND to a free GND on the UNO
  • Bluetooth adaptor Vcc to a free 5V on the UNO

NOTE: to program the UNO, you will need to leave the 5V line disconnected from the Bluetooth adaptor until done.

Step 10: Wiring the Batteries

  • Connect the POS (+) of the 11v battery pack to the POS (+) of the RoboClaw motor power in.
  • Connect the POS (-) of the 11v battery pack to the POS (-) of the RoboClaw motor power in.
  • Connect the POS (+) of the 6v battery pack to the POS (+) of the Pololu servo power in.
  • Connect the POS (-) of the 6v battery pack to the POS (-) of the Pololu servo power in.
  • Connect the USB or other 6v or more pwer bank to the UNO via the USB port or 2.1mm power jack.

See the RoboClaw and Pololu servo controller manuals for more info on Pin mapping.

Step 11: Loading the Code

NOTE: make sure the 5v line to the Bluetooth adaptor is not connected during this step.

With the wiring checked and rechecked, It's time to load the code. Follow the instructions on the Arduino site on how to install the Arduino IDE.

Now that the IDE and Motor driver library have been installed, load the robot code into the Arduino IDE from the zip file found in STEP 1 named boombot_roboclaw.ino.zip. These files are also included in this step as well.

Connect your PC and Arduino up with the USB cable. Now choose the board from the Tools->Board menu in the IDE, Uno for this project (choose the board you have if different from Uno) Now from Tools->Port menu, pick your com port. After this is done, click the upload button. If all went well, the code was loaded, if not see here for help on the IDE and related issues.

NOTE: Reconnect the 5v line from the UNO to the Bluetooth adaptor.

Step 12: Controlling the Bot From the Python Control Application

To run the Python remote control application, download the zip file from this step and extract it to the location you want to run it from. Next, open a command shell (i.e. terminal, cmd.exe, etc...) and navigate to the directory you extracted the file to. Now type: python rover.py from the command line and a window looking like the one in the image should pop up. If not, look for any python errors and correct (i.e. missing libraries etc...) The Python site may help some if needed.

Once the App is running, you should be ready go.

Now, power on the bot. Then type the comm port of your adaptor and click the "Connect" button. After a few seconds you should see the other controls enabled and the connect button disabled. This means you are connected to the robot. If you can't connect to the bluetooth adaptor, you will have to use the magic of Google to help you out!

The control application is simple to use and allows for mouse or keyboard controls.

The keyboard controls are:

  • Arrows are forward, reverse, turn left 90 degrees, turn right 90 degrees
  • a - turn left 45 degrees
  • s - turn right 45 degrees
  • h - halt (stop)
  • y - set Gripper Y axis
  • u - set gripper open/close
  • c - gripper home

Forward and Reverse commands are constant i.e. they keep the bot moving after execution until a new direction is sent or a Halt command.

The turns 90 and 45 degrees are temporary i.e. After some delay, they stop the bot from moving.

The gripper sliders automatically set the gripper on the bot. After you release the slider, the execution of the set is sent to the bot.

The Slider values range from 0-180.

  • Gripper Y Axis: 0 is All the way up and 180 is all the way down.
  • Gripper Open/Close: 0 is all the way closed and 180 and all the way open.

Use the "Disconnect" button to stop using the program. This will send commands to stop the bot and home the gripper.

Step 13: Controlling the Bot From an Android Phone/tablet

To crontrol the bot with an Android phone or tablet, install the application Robot Bluetooth Control to your device.

Once installed, you will have to pair your bot with the Android device you are using. After that is completed, open the Robot control app and add the key definitions like the images included here shows.

Step 14: Code Listing for the Robot Driver


/*
   Bluetooth Boom-Gripper bot.

   Goal in life...
      Follows your commands sent magically through the air! Or from USB :)

   Written by Scott Beasley - 2015
   Free to use or modify. Enjoy.
*/

#include 

// Various time delays used for driving and servo
#define TURNDELAY 475
#define TURNDELAY45 235

// Pololu servo controller commands
#define SETSPEED 0x01
#define SETPOS 0x04

/*
   Globals area.
*/

// Create the motor, servo objects to interface with
SoftwareSerial Serial_servo (10, 11);
SoftwareSerial Serial_motors (10, 12);

void setup ( )
{
   // Change the baud rates here if different than below
   Serial.begin (9600); // Bluethooth connection
   Serial_servo.begin (9600);
   Serial_motors.begin (19200);

   halt ( );
   set_servo_speed (0, 10); // Set the servo movement speed
   set_servo_speed (1, 10); // to make them move a bit smoother
   home_boom ( );
   home_gripper ( );

   delay (500);
}

void loop ( )
{
   byte command = 0, val = 0;
   // The following need to stay intact during the life of the 
   // session so they are made 'static'. This keeps us from 
   // having to have them as globals
   static byte dist = 1; // Start with short moves
   static byte speed = 1; // slow speed
   static byte speed_offset = 40;
   static byte turn_style = 1; // and with hard turns
   static byte boom_val = 60;
   static byte gripper_val = 60;

   if (Serial.available ( ) > 0) {
      // read the incoming command byte
      command = Serial.read ( );
   }

   switch (command) {
      case 'w':
         go_forward (speed_offset);
         //Serial.println ("Going Forward");

         if (dist == 1) {
            delay (750);
            halt ( );
         } else if (dist == 2) {
            delay (1250);
            halt ( );
         }

         break;

      case 'z':
         go_backward (speed_offset);
         //Serial.println ("Going Backwards");

         if (dist == 1) {
            delay (750);
            halt ( );
         } else if (dist == 2) {
            delay (1250);
            halt ( );
         }

         break;

      case 'a':
         go_left (speed_offset, turn_style);
         delay (TURNDELAY);
         halt ( );
         //Serial.println ("Turning Left");
         break;

      case 's':
         go_right (speed_offset, turn_style);
         delay (TURNDELAY);
         halt ( );
         //Serial.println ("Turning Right");
         break;

      case 'q':
         go_left (speed_offset, turn_style);
         delay (TURNDELAY45);
         halt ( );
         //Serial.println ("Turning Left");
         break;

      case 'e':
         go_right (speed_offset, turn_style);
         delay (TURNDELAY45);
         halt ( );
         //Serial.println ("Turning Right");
         break;

      case 'h':
         halt ( );
         //Serial.println ("Halting");
         break;

      case '>':
         // Gripper X move sends servo set value
         val = Serial.read ( );
         gripper_val = val;
	 
         // We limit the value to real movemovent limits of the setup
         set_gripper (val);
         //Serial.println ("GripperX");
         break;

      case '^':
         // Gripper Y move sends servo set value
         val = Serial.read ( );
         boom_val = val;
	 
         // We limit the value to real movemovent limits of the setup
         set_boom (val);
         //Serial.println ("GripperY");
         break;

      case 'c':
	 gripper_val = 60;
	 boom_val = 60;
         home_boom ( );
         home_gripper ( );
	 
         //Serial.println ("GripperHome");
         break;

      case 'v':
         // Set the spped the bot will move; 1 = slowest, 3 = fastest
         speed = Serial.read ( );

         if (speed == 1) {
            speed_offset = 40;    // Slow
         } else if (speed == 2) {
            speed_offset = 20;    // Med
         } else if (speed == 3) {
            speed_offset = 0;     // Fast!
         }

         //Serial.println ("Set speed");
         break;

      case 'd':
         // Set how long the bot will move; 1 = short, 2 medium,
         // 3 = continuous
         dist = Serial.read ( );
         //Serial.println ("Move distance set");
         break;

      case 't':
         // Set the way the bot does turns, one wheel (soft) or two (hard)
         turn_style = Serial.read ( );

         //Serial.println ("Turn style");
         break;

      case '+': // Speed set from phone
         speed++;
         if (speed >= 4)
            speed = 1;
	     
         if (speed == 1) {
            speed_offset = 40;    // Slow
         } else if (speed == 2) {
            speed_offset = 20;    // Med
         } else if (speed == 3) {
            speed_offset = 0;     // Fast!
         }
	     
         break;

      case '-': // Dist moved from phone
         dist++;
	  
         if (dist >= 4)
            dist = 1;
         //Serial.println ("Move distance set");
         break;

      case ',': // Open Gripper from phone
         gripper_val += 10;
	  
         if (gripper_val >= 179)
            gripper_val = 179;

         set_gripper (gripper_val);

         //Serial.println ("Gripper set");
         break;

      case '.': // Close Gripper from phone
         gripper_val -= 10;
	  
         if (gripper_val <= 0)
            gripper_val = 0;

         set_gripper (gripper_val);

         //Serial.println ("Gripper set");
         break;

      case '0': // Lower boom from phone
         boom_val += 10;
	  
         if (boom_val >= 179)
            boom_val = 179;

         set_boom (boom_val);

         //Serial.println ("Boom set");
         break;

      case '1': // Raise boom from phone
         boom_val -= 10;
	  
         if (boom_val <= 0)
            boom_val = 0;

         set_boom (boom_val);

         //Serial.println ("Boom set");
         break;

      case 255: // Sent after all gripper commands
         Serial.flush ( );
         break;
   }

   Serial.flush ( );
   delay(125);
}

void go_forward (byte speed_offset)
{
   //Serial.println ("Going forward...");
   Serial_motors.write ((byte)1 + speed_offset);
   Serial_motors.write ((byte)128 + speed_offset);
}

void go_backward (byte speed_offset)
{
   //Serial.println ("Going backward...");
   Serial_motors.write ((byte)127 - speed_offset);
   Serial_motors.write ((byte)255 - speed_offset);
}

void go_left (byte speed_offset, byte turn_style)
{
   //Serial.println ("Going left...");
   if (turn_style == 1)
      Serial_motors.write ((byte)1 + speed_offset);
   else
      Serial_motors.write ((byte)64); // Turn off the left motor (soft turn)

   Serial_motors.write ((byte)255 - speed_offset);
}

void go_right (byte speed_offset, byte turn_style)
{
   //Serial.println ("Going right...");

   Serial_motors.write ((byte)127 - speed_offset);

   if (turn_style == 1)
      Serial_motors.write ((byte)128 + speed_offset);
   else
      Serial_motors.write ((byte)192); // Turn off the right motor (soft turn)
}

void halt ( )
{
   //Serial.println ("Halt!");
   Serial_motors.write ((byte)0);
}

// Set a servos position
//   servo_id is the servo number (0-7)
//   angle is the abs position from 500 to 5500
void move_servo (int servo_id, int angle)
{
   unsigned char buff[6] = {0x80, 0x01, SETPOS, 0x00, 0x00, 0x00};

   unsigned int temp;
   unsigned char data_hi, data_low;

   temp = angle & 0x1f80;
   data_hi = temp >> 7;
   data_low = angle & 0x7f;

   buff[3] = servo_id; //servo number
   buff[4] = data_hi; //data1
   buff[5] = data_low; //data2

   Serial_servo.write (buff, 6);
   Serial_servo.flush ( );
}

void set_servo_speed (int servo_id, int speed)
{
   unsigned char buff[5] = {0x80, 0x01, SETSPEED, 0x00, 0x00};
   if (speed > 127)
      speed = 127;

   buff[3] = lowByte (servo_id);
   buff[4] = lowByte (speed);
   Serial_servo.write (buff, 5);
   Serial_servo.flush ( );
}

void set_boom (byte angle)
{
   move_servo (0, map (angle, 0, 179, 2500, 4000));
}

void set_gripper (byte angle)
{
   move_servo (1, map (angle, 0, 179, 2500, 4000));
}

void home_boom ( )
{
   move_servo (0, 3000);
}

void home_gripper ( )
{
   move_servo (1, 3000);
}

Step 15: Code Listing for the Python Application


#!/usr/bin/env python
#
# For Linux, BSD or Mac OSX you can chmod +x on this script to make executable
#
###########
# Rover control app
#
# Allows for simple multi-byte commands.
#
# Written by Scott Beasley - 2015
# Free to use or modify. Enjoy.
#
###########

import sys
import serial
import time
from Tkinter import *
import tkFont
import tkMessageBox

# Create the window for the application
class App (Frame):
    # Make the window
    def createWidgets (self):
        self.connected = False
        self.message = StringVar ( )

        # Make a little font for the gripper set buttons.
        helv6 = tkFont.Font (family = 'Helvetica',
                             size = 6, weight = 'normal')

        self.frame = Frame (self.master)
        self.frame.pack ( )

        self.f1 = Frame (self.frame)
        self.l1 = Label (self.f1, text = "Comm Port: ")
        self.l1.pack (side = LEFT)
        self.comm_entry = Entry (self.f1, bd = 5, name = "comm_entry")
        self.comm_entry.pack (side = LEFT)
        self.connectButton = Button (self.f1, text = "Connect",
                                    command = self.SerialConnect,
                                    name = "b_connect")
        self.connectButton.pack (side = LEFT)
        self.disconnectButton = Button (self.f1, text = "Disconnect",
                                    command = self.SerialDisconnect,
                                    name = "b_disconnect")
        self.disconnectButton.pack (side = RIGHT)
        self.f1.grid (row = 0, column = 0)

        self.f2 = LabelFrame (self.frame, bd = 3, relief = "groove",
                              text="Ground Control")

        self.g_vert_fm = Frame (self.f2)
        self.grip_vert = Scale (self.g_vert_fm, from_ = 0, to = 180)
        # Bind on mouse button 1 release to set the boom position
        self.grip_vert.bind ('', self.GripperY)
        self.grip_vert.grid (row = 0, column = 0, rowspan = 4, sticky = W)
        self.master.bind ("", self.GripperY)
        self.g_vert_fm.grid (row = 0, column = 0, rowspan = 4, sticky = W)

        self.leftforwardButton = Button (self.f2, text = "\\",
                                 command = self.TurnLeft45,
                                 name = "b_left_forward")
        self.leftforwardButton.grid (row = 0, column = 1)
        self.master.bind ("<a rel="nofollow">", self.TurnLeft45)

        self.leftButton = Button (self.f2, text = "<",
                                 command = self.TurnLeft, name = "b_left")
        self.leftButton.grid (row = 1, column = 1)
        self.master.bind ("</a><a rel="nofollow">", self.TurnLeft)

        self.rightforwardButton = Button (self.f2, text = "/",
                                 command = self.TurnRight45,
                                 name = "b_right_forward")
        self.rightforwardButton.grid (row = 0, column = 3)
        self.master.bind ("</a><a rel="nofollow">", self.TurnRight45)

        self.haltButton = Button (self.f2, text = "Halt!",
                                 command = self.Halt, name = "b_halt")
        self.haltButton.grid (row = 1, column = 2)
        self.master.bind ("</a><a rel="nofollow">", self.Halt)

        self.rightButton = Button (self.f2, text=">",
                                  command = self.TurnRight, name = "b_right")
        self.rightButton.grid(row = 1, column = 3)
        self.master.bind ("</a><a rel="nofollow">", self.TurnRight)

        self.upButton = Button (self.f2, text="^",
                               command = self.Forward, name = "b_forward")
        self.upButton.grid (row = 0, column = 2)
        self.master.bind ("</a><a rel="nofollow">", self.Forward)

        self.leftdownButton = Button (self.f2, text = "/",
                                      command = self.TurnRight45,
                                      name = "b_left_down")
        self.leftdownButton.grid (row = 2, column = 1)

        self.downButton = Button (self.f2, text="V",
                                  command = self.Reverse, name = "b_reverse")
        self.downButton.grid (row = 2, column = 2)
        self.master.bind ("</a><a rel="nofollow">", self.Reverse)
        self.f2.grid (row = 1, column = 0, pady = 25)

        self.rightdownButton = Button (self.f2, text = "\\",
                                       command = self.TurnLeft45,
                                       name = "b_right_down")
        self.rightdownButton.grid (row = 2, column = 3)

        self.g_horz_fm = Frame (self.f2)
        self.grip_horz = Scale (self.g_horz_fm, from_ = 0, to = 180,
                                orient = HORIZONTAL)
        # Bind on mouse button 1 release to set the gripper position
        self.grip_horz.bind ('</a><a rel="nofollow">', self.GripperX)
        self.grip_horz.grid (row = 0, column = 0, columnspan = 7, sticky = E)

        self.master.bind ("<u>", self.GripperX)
        self.g_horz_fm.grid (row = 4, column = 0, columnspan = 7, sticky = E)
        self.master.bind ("</u></a><a rel="nofollow"><u>", self.GripperHome)

        self.f_spd_dist = Frame (self.frame)
        self.l_speed = Label (self.f_spd_dist, text = "  Speed: ")
        self.l_speed.grid (row = 0, column = 0)
        self.speed_spin = Spinbox (self.f_spd_dist, values = (1, 2, 3),
                                   width = 2, command = self.SetSpeed)
        self.speed_spin.grid (row = 0, column = 1)
        self.l_dist = Label (self.f_spd_dist, text = "  Distance: ")
        self.l_dist.grid (row = 0, column = 3)
        self.dist_spin = Spinbox (self.f_spd_dist,
                                  values = ('Short', 'Medium', 'Continuous'),
                                  width = 10, command = self.SetDistance)
        self.dist_spin.grid (row = 0, column = 4)
        self.l_turns = Label (self.f_spd_dist, text = "  Turns: ")
        self.l_turns.grid (row = 0, column = 5)
        self.turns_spin = Spinbox (self.f_spd_dist,
                                   values = ('Hard', 'Soft'),
                                   width = 5, command = self.SetTurns)
        self.turns_spin.grid (row = 0, column = 6)
        self.f_spd_dist.grid (row = 2, column = 0)

        self.f3 = Frame (self.frame)
        self.l2 = Label (self.f3, text = "Last action: ")
        self.l2.pack (side = LEFT)
        self.l3 = Label (self.f3, text=" ", textvariable = self.message)
        self.l3.pack (side = RIGHT)
        self.f3.grid (row = 5, column = 0, pady = 8)

    # Set the state of the bot control buttons. Enable when connected,
    # Disabled otherwise.
    def CtrlButtonsState (self, bstate):
        self.leftforwardButton.config (state = bstate)
        self.leftButton.config (state = bstate)
        self.rightforwardButton.config (state = bstate)
        self.rightButton.config (state = bstate)
        self.upButton.config (state = bstate)
        self.leftdownButton.config (state = bstate)
        self.downButton.config (state = bstate)
        self.rightdownButton.config (state = bstate)
        self.haltButton.config (state = bstate)
        self.disconnectButton.config (state = bstate)
        self.grip_horz.config (state = bstate)
        self.grip_vert.config (state = bstate)

    # Set the state of the comm port entry. Enable when not connected,
    # Disabled when the bot is connected.
    def ConnCtrlsState (self, bstate):
        self.connectButton.config (state = bstate)
        self.comm_entry.config (state = bstate)

    # Connect to the comm port typed in the comm entry field.
    def SerialConnect (self):
        try:
            # Change the baud rate here if diffrent then 9600
            self.ser = serial.Serial (self.comm_entry.get ( ), 9600)
        except IOError:
            tkMessageBox.showerror ("Invalid comm port", "Comm port not found.")
            return

        self.ConnCtrlsState (DISABLED)
        self.CtrlButtonsState (NORMAL)
        self.message.set ("SerialConnect")
        self.connected = True
        time.sleep (3) # Dwell a bit for the connection to happen

        # Do some after connection bot talking.
        if self.connected == True:
            self.setbotParms ( )

    # Disconnect from the bot (close the comm port).
    def SerialDisconnect (self):
        try:
            # Send a Halt command just in case the bot is still moving.
            self.send_cmd ('h', "Halt!")
            time.sleep (1)
            self.ser.close ( )
        except IOError:
            print "Could not close port..."

        self.message.set ("SerialDisconnect")
        self.ConnCtrlsState (NORMAL)
        self.CtrlButtonsState (DISABLED)
        self.connected = False
        time.sleep (2) # Dwell a bit for the disconnection to happen

    # Send the command to the open comm port
    # the action input variable is a tuple that allows for multy part commands
    def send_cmd (self, action, msg):
        if self.connected == True:
            for val in action:
                self.ser.write (val)
            self.ser.flush ( )
            self.message.set (msg)

    # Send the bot a turn-left command.
    def TurnLeft (self, event = None):
        self.send_cmd ('a', "Left")

    # Send the bot a turn-left-up command.
    def TurnLeft45 (self, event = None):
        self.send_cmd ('q', "Left45")

    # Send the bot a turn-right command.
    def TurnRight (self, event = None):
        self.send_cmd ('s', "Right")

    # Send the bot a turn-right-up command.
    def TurnRight45 (self, event = None):
        self.send_cmd ('e', "Right45")

    # Send the bot a Forward command.
    def Forward (self, event = None):
        self.send_cmd ('w', "Up")

    # Send the bot a Reverse command.
    def Reverse (self, event = None):
        self.send_cmd ('z', "Down")

    # Send the bot a Halt command.
    def Halt (self, event = None):
        self.send_cmd ('h', "Halt!")

    # Set the gripper (X).
    def GripperX (self, event = None):
        # Read the slider control and send the value to the bot controller
        # Note: 0 is all the way closed and 180 is all the way open
        # This is a multi-byte command. The command + the servo set val
        # + a buffer clear command (optional)
        grp_change = ('>', chr (self.grip_horz.get ( )), chr (255))
        self.send_cmd (grp_change, "Gripper X")
        self.boomchange = None

    # Set the gripper Y.
    def GripperY (self, event = None):
        # Read the slider control and send the value to the bot controller
        # Note: 0 is all the way up and 180 is all the way down
        # This is a multi-byte command. The command + the servo set val
        # + a buffer clear command (optional)
        grp_change = ('^', chr (self.grip_vert.get ( )), chr (255))
        self.send_cmd (grp_change, "Gripper Y")
        self.gripperchange = None

    # Set the gripper to the "home" position.
    def GripperHome (self, event = None):
        self.send_cmd (('c', chr (255)), "Gripper Home")
        self.grip_horz.set (90)
        self.grip_vert.set (90)

    # Set the speed of the rover.
    def SetSpeed (self, event = None):
        spd_str = self.speed_spin.get ( )

        if spd_str == "1":
            speed = 1
        if spd_str == "2":
            speed = 2
        if spd_str == "3":
            speed = 3

        # Sends a multi-byte command to set the speed.
        self.send_cmd (('v', chr (speed)), "Set Speed")

    # Set the distance travled when a for or rev is issued to 
    # the rover.
    def SetDistance (self, event = None):
        dist = 1
        dist_str = self.dist_spin.get ( )

        if dist_str == "Short":
            dist = 1
        if dist_str == "Medium":
            dist = 2
        if dist_str == "Continuous":
            dist = 3

        # Sends a multi-byte command to set the distance.
        self.send_cmd (('d', chr (dist)), "Set Distance")

    # Set the turn style to use turning.
    def SetTurns (self, event = None):
        turn = 1
        turn_str = self.turns_spin.get ( )

        if turn_str == "Hard":
            turn = 1
        if turn_str == "Soft":
            turn = 2

        # Sends a multi-byte command to set the turn style.
        self.send_cmd (('t', chr (turn)), "Set Turns")

    def setbotParms (self):
        self.Halt ( )
        self.GripperHome ( )
        self.SetSpeed ( )
        self.SetDistance ( )
        self.SetTurns ( )
        
    def __init__ (self, master = None):
        Frame.__init__ (self, master)
        self.pack ( )
        self.createWidgets ( )
        self.CtrlButtonsState (DISABLED)

# Kick off the GUI (Tk) then size and title the app window
def main ( ):
    root = Tk ( )
    root.geometry ("450x360")
    root.wm_title ("Rover Control Center (RCC)")
    app = App (master = root)
    app.mainloop ( )

if __name__ == '__main__':
    main ( )
</u></a>