Python + Arduino: GUI Control through Serial

I have created a basic gui program that allows me to move the arm to particular positions by controlling the angle of the servos. Scale bars created in python using Tkinter define the angle of the servo. Those angles are then sent to the arduino which uses the servo control function created earlier in the project to move the servos to the new desired position. For the moment the "move arm" button must be pressed to send a new value. It is very simple to have the arm follow the trackbar position live and that will come next, but for the moment this piecemeal layout makes the operations clearer.

Python Gui created in Tkinter




Below is the arduino code. 

#include <Servo.h>  //arduino library
#include <math.h>   //standard c library

#define PI 3.141

Servo baseServo;
Servo shoulderServo;
Servo wristServo;

int command;

struct jointAngle{
  int base;
  int shoulder;
  int elbow;
};

struct jointAngle desiredAngle; //desired angles of the servos

//+++++++++++++++FUNCTION DECLARATIONS+++++++++++++++++++++++++++

void servoControl (int thePos, int theSpeed, Servo theServo);

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++




void setup()
{
  Serial.begin(9600);
  baseServo.attach(9);        // attaches the servo on pin 9 to the servo object
  shoulderServo.attach(10);
  wristServo.attach(11);

  Serial.setTimeout(50);      //ensures the the arduino does not read serial for too long
  Serial.println("started");
  baseServo.write(90);        //intial positions of servos
  shoulderServo.write(150);
  wristServo.write(110);
}

//primary arduino loop
void loop()
{
  while (Serial.available()){
    //read a serial command of the type (1,2,3d) where they are the joint angles for the position of the arm.

    desiredAngle.base = Serial.parseInt();
    desiredAngle.shoulder = Serial.parseInt();
    desiredAngle.elbow = Serial.parseInt();

    if(Serial.read() == 'd'){               // if the last byte is 'd' then stop reading and execute command 'd' stands for 'done'
        Serial.println("received signal");
           
    }

    //move the servo to the desired position
    servoControl(desiredAngle.base, 20, baseServo);
    servoControl(desiredAngle.shoulder, 20, shoulderServo);
    servoControl(desiredAngle.elbow, 20, wristServo);    

    Serial.println("done");
  } // end of serial while

}

//++++++++++++++++FUNCTION DEFITNITIONS++++++++++++++++++++++++++

void servoControl (int thePos, int theSpeed, Servo theServo){
  //thePos is the desired position the servo should be driven to
  // theSpeed is the delay between each increment of the servo position in milliseconds
  // theServo is the servo object that is to be controled.

  Serial.println("in servoControl");

  int startPos = theServo.read();        //read the current pos
  int newPos = startPos;

  //define where the pos is with respect to the command
  // if the current position is less that the actual move up
  if (startPos < thePos){
        while (newPos < (thePos - 5)){
          newPos = newPos + 5;
          theServo.write(newPos);
          delay(theSpeed);
        }  
  }

  else{
       while (newPos > (thePos + 5)){
         newPos = newPos - 5;
         theServo.write(newPos);
         delay(theSpeed);
       }  
  }
} // end of function


Below is the python code used to create the gui and serial communications. While the gripper buttons were unused they will be implemented later in the project.

from Tkinter import *
import tkMessageBox
import time
import serial

#+++++++++++++Variables+++++++++++++++++++++
ser = serial.Serial('/dev/ttyACM0', 9600)


#++++++++++++++++Functions+++++++++++++++++++++++
def open_gripper ():
    tkMessageBox.showinfo( "Hello Python", "Gripper Open")
    #ser.write(str(1))
    return

def close_gripper ():
    tkMessageBox.showinfo( "Hello Python", "Gripper closed")
    return


def move_it():
    #this function sends the command of joint angles to the arduino to move the servos to the desired positions

    command = str(base.get()) + ',' + str(shoulder.get()) +   ',' + str(elbow.get() ) + 'd'
    print command
    ser.write(command)
    #if not in the commandline can print verification to a notification window
    #tkMessageBox.showinfo( "Hello Python", "message sent")


#++++++++++++++++++++The GUI++++++++++++++++++++++
root = Tk()

#++++++++++++++++++++Drive Motors++++++++++++++++++

motorControl = Frame(root)
motorControl.pack()

forwardFrame = Frame(motorControl)
forwardFrame.pack()

backFrame = Frame(motorControl)
backFrame.pack (side = BOTTOM)

speedControl = Frame(root)
speedControl.pack()


#+++++++++++++++++ARM+++++++++++++++++++++++++
# The scroll bars
armControl = Frame(root)
armControl.pack( )

armLabel = Label(armControl, text = "Arm Componets", bg = "red", padx = 100)
armLabel.pack()

#++++++++++++++++++++++++BASE+++++++++++++++++++++++++++

baseLabel = Label(armControl, text = "Base", bg = "green", padx = 100)
baseLabel.pack()
base = Scale(armControl, from_= 1, to = 175, length = 300, orient = HORIZONTAL)
base.pack()

#++++++++++++++++++++++++Shoulder+++++++++++++++++++++++++
shoulderLabel = Label(armControl, text = "shoulder", bg = "green", padx = 100)
shoulderLabel.pack()
shoulder = Scale(armControl, from_= 1, to = 175, length = 300, orient = HORIZONTAL)
shoulder.pack()

#++++++++++++++++++++++ELBOW++++++++++++++++++++++++++++
elbowLabel = Label(armControl, text = "elbow", bg = "green", padx = 100)
elbowLabel.pack()
elbow = Scale(armControl, from_= 1, to = 175, length = 300, orient = HORIZONTAL)
elbow.pack()

#Send
armGoBut = Button(armControl, text ="Move arm", height = 2, width = 5, command = move_it  )
armGoBut.pack()

#++++++++++++++++++++++++++++Gripper+++++++++++++++++++

gripperFrame = Frame(root)
gripperFrame.pack()


gripperLabel = Label(gripperFrame, text = "Gripper", bg = "green", padx = 20)
gripperLabel.pack()


gripOpenBut = Button(gripperFrame, text ="Open", height = 2, width = 5, command = open_gripper  )
gripOpenBut.pack(side = RIGHT)

gripCloseBut = Button(gripperFrame, text ="Close", height = 2, width = 5,command = close_gripper )
gripCloseBut.pack(side = RIGHT)

#+++++++++++++++++++++++++++Read Values+++++++++++++++++

root.mainloop()


2 comments: