Scott Beasley
Published © CC BY-SA

Remote Control Gripper Bot

This is a simple to build, fun 4WD bluetooth controlled robot with a 2DOF gripper and lot's of room to grow!

IntermediateFull instructions provided4 hours9,791
Remote Control Gripper Bot

Things used in this project

Hardware components

Runt Rover Half-pint chassis
×1
Arduino UNO
Arduino UNO
×1
V5 Shield
×1
Female/Female Jumper Wires
Female/Female Jumper Wires
×1
L9110S
×1
90° Single Angle Channel Bracket (585424)
×1
Light weight servo hub, Futaba
×1
Standard Gripper Kit A (637094)
×1
MG995
×1
Standard Servo plate
×1
90° Single Angle Channel Bracket
×1
5 slot AA holder
×1
AA Rechagable batteries
×5
.750" 8-32 nylon spacers
×2
.500" 6-32 screws with nuts
×8
.3125" 6-32 screws
×8

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Custom parts and enclosures

4wdgripperbt2.fzz

Schematics

4wdgripperbt_bb.png

4wdgripperbt2.fzz

Code

Python Driver GUI

Python
#!/usr/bin/env python
#
# For Linux, BSD or Mac OSX you can chmod +x on this script to make executable
#
###########
# Rover control app
#
# 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)
        self.grip_vert.grid (row = 0, column = 0, rowspan = 4, sticky = W)
        self.grip_vert_set = Button (self.g_vert_fm, text = "Set",
                                     command = self.GripperY,
                                     name = "b_grip_vert_set",
                                     width = 1, height = 2,
                                     font = helv6)
        self.grip_vert_set.grid (row = 5, column = 0, 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)
        self.grip_horz.grid (row = 0, column = 0, columnspan = 7, sticky = E)

        self.grip_horz_set = Button (self.g_horz_fm, text = "Set",
                                     command = self.GripperX,
                                     name = "b_grip_horz_set",
                                     width = 1, height = 2,
                                     font = helv6)
        self.grip_horz_set.grid (row = 0, column = 8)
        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.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 = 3, 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)
        self.grip_horz_set.config (state = bstate)
        self.grip_vert_set.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

    # 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
    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
        grp_change = ('>', chr (self.grip_horz.get ( )), chr (255))
        self.send_cmd (grp_change, "Gripper X")

    # 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
        grp_change = ('^', chr (self.grip_vert.get ( )), chr (255))
        self.send_cmd (grp_change, "Gripper Y")

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

    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 ("450x350")
    root.wm_title ("Rover Control Center (RCC)")
    app = App (master = root)
    app.mainloop ( )

if __name__ == '__main__':
    main ( )

Rover code

Arduino
/*
   Bluetooth Rover.

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

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

/*
   Uses the L9110S library. It works with the L9110S h-bridge.

   Download from https://github.com/jscottb/L9110Driver or clone the zip from
   https://github.com to remove the '-master' from the archive file name to add
   the library
*/

#include <L9110Driver.h>
#include <Servo.h>

#define SERVO_Y 2 // Pin gripper Y servo
#define SERVO_CLAW 7 // Pin gripper Gripper servo
#define pinAIN1 5 // define I1 interface
#define pinAIN2 6 // define I2 interface
#define pinBIN1 3 // define I3 interface
#define pinBIN2 11 // define I4 interface

// Speed defines
#define MAXFORWARDSPEED 225 // Max speed we want moving forward
#define MAXBACKWARDSPEED 225 // Max reverse speed
#define TOPSPEED 255 // Used to help turn better on carpet and rougher surfaces.

// Various time delays used for driving and servo
#define TURNDELAY 475
#define TURNDELAY45 235
#define BACKUPDELAY 400
#define SERVOMOVEDELAY 200
#define SERVOSEARCHDELAY 85

/*
   Globals area.
*/

// Create the motor, servo objects to interface with
L9110_Motor motor_left (pinAIN1, pinAIN2); // Create Left motor object
L9110_Motor motor_right (pinBIN1, pinBIN2); // Create Right motor object
Servo grip_y_servo; // Create a servo object for the gripper Y axis
Servo grip_servo; // Create a servo object for the gripper claw

void setup ( )
{
   // Change the baud rate here if different then 9600
   Serial.begin (9600);

   grip_y_servo.attach (SERVO_Y); // Attach the servo SERVO_LR
   grip_y_servo.write (90);
   grip_servo.attach (SERVO_CLAW); // Attach the servo SERVO_LR
   grip_servo.write (90);

   delay (500);
}

void loop ( )
{
   byte command = 0, val = 0;

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

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

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

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

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

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

      case 'e':
         go_right ( );
	       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 ( );

         // We limit the value to real movement limits of the setup
         grip_servo.write (constrain (val, 64, 179));
         //Serial.println ("GripperX");
         break;

      case '^':
         // Gripper Y move sends servo set value
         val = Serial.read ( );

         // We limit the value to real movement limits of the setup
         grip_y_servo.write (constrain (val, 53, 179));
         //Serial.println ("GripperY");
         break;

      case 'c':
         // We limit the value to real movement limits of the setup
         grip_y_servo.write (90);
         grip_servo.write (90);
         //Serial.println ("GripperHome");
         break;

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

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

void go_forward ( )
{
   //Serial.println ("Going forward...");

   // Ramp the motors up to the speed.
   // Help with spinning out on some surfaces and ware and tare on the GM's
   ramp_it (MAXFORWARDSPEED, FORWARD, FORWARD);

   // Set to all of the set speed just incase the ramp last vat was not all of
   // it.
   motor_left.setSpeed (MAXFORWARDSPEED);
   motor_right.setSpeed (MAXFORWARDSPEED);
   motor_left.run (FORWARD|RELEASE);
   motor_right.run (FORWARD|RELEASE);
}

void go_backward ( )
{
   //Serial.println ("Going backward...");

   // Ramp the motors up to the speed.
   // Help with spinning out on some surfaces and ware and tare on the GM's
   ramp_it (MAXBACKWARDSPEED, BACKWARD, BACKWARD);

   // Set to all of the set speed just incase the ramp last vat was not all of
   // it.
   motor_left.setSpeed (MAXBACKWARDSPEED);
   motor_right.setSpeed (MAXBACKWARDSPEED);
   motor_left.run (BACKWARD|RELEASE);
   motor_right.run (BACKWARD|RELEASE);
}

void go_left ( )
{
   //Serial.println ("Going left...");

   // Ramp the motors up to the speed.
   // Help with spinning out on some surfaces and ware and tare on the GM's
   ramp_it (TOPSPEED, BACKWARD, FORWARD);

   // Set to all of the set speed just incase the ramp last vat was not all of
   // it.
   motor_left.setSpeed (TOPSPEED);
   motor_right.setSpeed (TOPSPEED);
   motor_left.run (BACKWARD|RELEASE);
   motor_right.run (FORWARD|RELEASE);
}

void go_right ( )
{
   //Serial.println ("Going right...");

   // Ramp the motors up to the speed.
   // Help with spinning out on some surfaces and ware and tare on the GM's
   ramp_it (TOPSPEED, FORWARD, BACKWARD);

   // Set to all of the set speed just incase the ramp last vat was not all of
   // it.
   motor_left.setSpeed (TOPSPEED);
   motor_right.setSpeed (TOPSPEED);
   motor_left.run (FORWARD|RELEASE);
   motor_right.run (BACKWARD|RELEASE);
}

void halt ( )
{
   //Serial.println ("Halt!");
   //ramp_it (0, BRAKE, BRAKE);
   motor_left.setSpeed (0);
   motor_right.setSpeed (0);
   motor_left.run (BRAKE);
   motor_right.run (BRAKE);
}

void ramp_it (uint8_t speed, uint8_t lf_dir, uint8_t rt_dir)
{
  uint8_t ramp_val = 0, step_val = 0;

  step_val = abs (speed / 4);
  if (!speed)
     step_val = -step_val;

  for (uint8_t i = 0; i < 4; i++) {
     ramp_val += step_val;
     motor_left.setSpeed (ramp_val);
     motor_right.setSpeed (ramp_val);
     motor_left.run (lf_dir|RELEASE);
     motor_right.run (rt_dir|RELEASE);
     delay (25);
  }
}

L9110 Driver

L9110 motor driver for Arduino

Credits

Scott Beasley

Scott Beasley

9 projects • 61 followers
Well odd

Comments