diff --git a/controller/relayer.py b/controller/relayer.py
new file mode 100755
index 0000000000000000000000000000000000000000..6fec5c32b6cbe5587e88e670cfe1137ab9f1a658
--- /dev/null
+++ b/controller/relayer.py
@@ -0,0 +1,96 @@
+#!/usr/bin/python
+
+import serial
+import struct
+import pygame
+import math
+import socket
+
+#################################################
+#   BEGIN SETTINGS
+#################################################
+
+# Serial
+#PORT = '/dev/ttyUSB0'
+PORT = '/dev/ttyACM0'
+BAUDRATE = 57600
+TIMEOUT = 0.2
+LIVE = True
+
+# Initialize Serial
+if LIVE:
+	ser = serial.Serial()
+	ser.port = PORT
+	ser.baudrate = BAUDRATE
+	ser.timeout = TIMEOUT
+	ser.write_timeout = 1
+
+	ser.open()
+
+HOST = ''                 # Symbolic name meaning all available interfaces
+PORT = 50007              # Arbitrary non-privileged port
+s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+s.bind((HOST, PORT))
+print 'listening for sender...'
+s.listen(1)
+conn, addr = s.accept()
+print 'Connected by', addr
+while 1:
+    data = conn.recv(4096)
+    print data
+    #data = map(int, data.split('\r\n')[0].split(',')) #.rstrip('\r\n')
+    data = data.rstrip('\r\n').split('\r\n')	#split each send as item in list
+    if (len(data) > 1 ):
+	print('I am not fast enough')
+	print data
+	print len(data)
+	data = 0
+	continue
+    data = data[0]
+    data = map(int, data.split(','))
+    print data
+    if (data[0] == 98 and data[11] == 101):
+	ser.reset_output_buffer()
+	ser.reset_input_buffer()
+    	ser.write(data)
+    if not data: break
+    data = 0
+
+    #conn.sendall(data)
+conn.close()
+
+
+
+# Main program loop
+run = True
+while run:
+
+	# Set mData based on jData
+	processInput(jData, jData2, mData, macros)
+
+	# Make sure data is bounded
+	sanityCheck(mData)
+
+	# Write data to serial
+	if LIVE:
+	    writeSerialData(ser, mData)
+
+	    # print("Reading")
+	    if ser.readline():
+		print("OK")
+	    else:
+		print("DATA LOSS")
+		mData.dataLoss += 1
+
+	drawText(s, jData, jData2, mData, macros)
+	drawMeters(s, jData, jData2, mData)
+
+	pygame.display.flip()
+	clock.tick(FRAMERATE)
+
+if LIVE:
+	if(ser.isOpen()):
+	    ser.close()
+
+if __name__ == "__main__":
+    main()
diff --git a/controller/sender_udp_v2.py b/controller/sender_udp_v2.py
new file mode 100755
index 0000000000000000000000000000000000000000..9acc9b7dddc4a8a38c3cf123aa2def1a9f047cfa
--- /dev/null
+++ b/controller/sender_udp_v2.py
@@ -0,0 +1,554 @@
+#!/usr/bin/python
+
+import serial
+import struct
+import pygame
+import math
+import socket
+
+# IP setup
+
+HOST = '10.9.0.3'    # The remote host
+PORT = 50007              # The same port as used by the server
+
+
+#################################################
+#   BEGIN SETTINGS
+#################################################
+
+# Pygame
+WIDTH = 800
+HEIGHT = 600
+FRAMERATE = 20
+
+# Serial
+#PORT = '/dev/ttyUSB0'
+#PORT = '/dev/ttyACM0'
+#BAUDRATE = 57600
+#TIMEOUT = 0.2
+LIVE = True
+
+# Joystick
+JTHRESH = 0.1
+JTHRESH_TURN = 0.01
+
+# Wat
+MACRO = pygame.USEREVENT + 1
+
+#################################################
+#   END SETTINGS
+#################################################
+
+# Joystick data
+class JoystickData:
+    numJoys = 0
+    macro = 0
+
+    def __init__(self):
+        self.buttons = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+        self.axis = [0.0, 0.0, -1.0, 0.0, 0.0, -1.0]
+        self.hat = (0, 0)
+        JoystickData.numJoys += 1
+
+# Motor data / Serial Data
+class MotorData:
+    numMotors = 0
+    macro = 0
+    macroRunning = 0
+    dataLoss = 0
+
+    def __init__(self):
+        self.lmotor = (0, 0)
+        self.rmotor = (0, 0)
+        self.servos = [0, 0, 0, 0, 0, 0]
+        MotorData.numMotors += 1
+
+class Macro:
+    numMacros = 0
+    timestep = 0
+
+    def __init__(self, macroid, name, actions):
+        self.macroid = macroid
+        self.name = name
+        self.actions = actions
+        Macro.numMacros += 1
+
+
+# data = bytearray(b'\x00' * (2 + 4 + 10))
+
+# Joystick Dictionary
+jBtnName = ['A', 'B', 'X', 'Y', 'LB', 'RB', 'Select', 'Start', 'Logo', 'LS', 'RS']
+jBtn = {jBtnName[x]: x for x in range(len(jBtnName))}
+
+jAxisName = ['LSx', 'LSy', 'LT', 'RSx', 'RSy', 'RT']
+jAxis = {jAxisName[x]: x for x in range(len(jAxisName))}
+
+# Sets jData based on detected events
+def handleJoyEvent(e, jData, jData2):
+    if e.joy == 0:
+        if e.type == pygame.JOYAXISMOTION:
+            jData.axis[e.axis] = e.value
+
+        if e.type == pygame.JOYBUTTONDOWN:
+            jData.buttons[e.button] = 1
+
+        if e.type == pygame.JOYBUTTONUP:
+            jData.buttons[e.button] = 0
+
+        if e.type == pygame.JOYHATMOTION:
+            jData.hat = e.value
+
+    if e.joy == 1:
+        if e.type == pygame.JOYAXISMOTION:
+            jData2.axis[e.axis] = e.value
+
+        if e.type == pygame.JOYBUTTONDOWN:
+            jData2.buttons[e.button] = 1
+
+        if e.type == pygame.JOYBUTTONUP:
+            jData2.buttons[e.button] = 0
+
+        if e.type == pygame.JOYHATMOTION:
+            jData2.hat = e.value
+
+    else:
+        pass
+
+# Sets mData based on jData
+def processInput(jData, jData2, mData, macros):
+    sfactor = 235 #100
+    turnfactor = 180
+    
+    # arm, claw, bottom, top, cagelock, droplock
+
+    #---------------------
+    #   JOYSTICK 0
+    #---------------------
+
+    # Hat
+    # if jData.hat[1] == 1:
+    #     mData.servos[3] += 2
+    # elif jData.hat[1] == -1:
+    #     mData.servos[3] -= 2
+    # elif jData.hat[0] == 1:
+    #     mData.servos[2] += 2
+    #     mData.servos[3] += 2
+    # elif jData.hat[0] == -1:
+    #     mData.servos[2] -= 2
+    #     mData.servos[3] -= 2
+
+    # Joystick buttons
+    if jData.buttons[jBtn['A']] == 1:
+        mData.servos[1] = 100
+    elif jData.buttons[jBtn['B']] == 1:
+        mData.servos[1] = 45
+
+    # Joystick triggers
+    if jData.axis[jAxis['LT']] > -1 + JTHRESH:
+        mData.servos[0] += 2
+    elif jData.axis[jAxis['RT']] > -1 + JTHRESH:
+        mData.servos[0] -= 2
+
+    # Prevent multiple macros
+    if mData.macroRunning == 0:
+        # Macros
+        if jData.buttons[jBtn['Y']] == 1:
+            mData.macro = 0
+            mData.macroRunning = 1
+            playMacro(macros[mData.macro], mData)
+            pygame.time.set_timer(MACRO, 100)
+        elif jData.buttons[jBtn['Start']] == 1:
+            mData.macro = 1
+            mData.macroRunning = 1
+            playMacro(macros[mData.macro], mData)
+            pygame.time.set_timer(MACRO, 100)
+        elif jData.buttons[jBtn['Select']] == 1:
+            mData.macro = 2
+            mData.macroRunning = 1
+            playMacro(macros[mData.macro], mData)
+            pygame.time.set_timer(MACRO, 100)
+
+    # Turbo mode
+    if jData.buttons[jBtn['RB']] == 1:
+        offset = 255 - sfactor
+    else:
+        offset = 10
+
+    '''
+    # R/L stick
+    if jData.axis[jAxis['LSy']] > 0 + JTHRESH:
+        mData.lmotor = (int(math.floor(sfactor * jData.axis[jAxis['LSy']]) + offset), 0)
+    elif jData.axis[jAxis['LSy']] < 0 - JTHRESH:
+        mData.lmotor = (int(-math.floor(sfactor * jData.axis[jAxis['LSy']]) + offset - 1), 1)
+    else:
+        mData.lmotor = (0, 0)
+
+    if jData.axis[jAxis['RSy']] > 0 + JTHRESH:
+        mData.rmotor = (int(math.floor(sfactor * jData.axis[jAxis['RSy']]) + offset), 0)
+    elif jData.axis[jAxis['RSy']] < 0 - JTHRESH:
+        mData.rmotor = (int(-math.floor(sfactor * jData.axis[jAxis['RSy']]) + offset - 1), 1)
+    else:
+        mData.rmotor = (0, 0)
+    '''
+    drive = (0, 0)
+    #print(drive)
+    
+    if jData.axis[jAxis['LSy']] > 0 + JTHRESH:
+        drive = (int(math.floor(sfactor * jData.axis[jAxis['LSy']]) + offset), 0)
+    elif jData.axis[jAxis['LSy']] < 0 - JTHRESH:
+        drive = (int(-math.floor(sfactor * jData.axis[jAxis['LSy']]) + offset - 1), 1)
+    else:
+        drive = (0, 1)
+
+    mData.lmotor = drive
+    mData.rmotor = drive
+
+    turn = (0,0)
+
+    if jData.axis[jAxis['RSx']] > 0 + JTHRESH_TURN:
+        turn = (int(math.floor(turnfactor * jData.axis[jAxis['RSx']])), 0)
+    elif jData.axis[jAxis['RSx']] < 0 - JTHRESH_TURN:
+        turn = (int(-math.floor(turnfactor * jData.axis[jAxis['RSx']])  - 1), 1)
+    else:
+        turn = (0, 1)
+
+    if drive != (0, 1):
+    	mData.lmotor = (sorted([0, (mData.lmotor[0] + pow(-1,turn[1])*turn[0]), 255])[1], mData.lmotor[1])
+    	mData.rmotor = (sorted([0, (mData.rmotor[0] - pow(-1,turn[1])*turn[0]), 255])[1], mData.rmotor[1])
+    else:
+    	mData.lmotor = (sorted([0, ( turn[0]), 245])[1], 1-turn[1])
+    	mData.rmotor = (sorted([0, ( turn[0]), 245])[1], turn[1])    	
+   
+    #---------------------
+    #   JOYSTICK 1
+    #---------------------
+
+    # Arm on LT and RT
+    if jData2.axis[jAxis['LT']] > -1 + JTHRESH:
+        mData.servos[0] += 2
+    elif jData2.axis[jAxis['RT']] > -1 + JTHRESH:
+        mData.servos[0] -= 2
+
+    # Claw on A and B
+    if jData2.buttons[jBtn['A']] == 1:
+        mData.servos[1] += 2
+    elif jData2.buttons[jBtn['B']] == 1:
+        mData.servos[1] -= 2
+
+    # Drop lock on X and Y
+    if jData2.buttons[jBtn['X']] == 1:
+        mData.servos[5] -= 2
+    elif jData2.buttons[jBtn['Y']] == 1:
+        mData.servos[5] += 2
+
+    # Cage control on HAT
+    if jData2.hat[1] == 1:
+        mData.servos[2] += 2
+        mData.servos[3] += 2
+    elif jData2.hat[1] == -1:
+        mData.servos[2] -= 2
+        mData.servos[3] -= 2
+
+    # Cage control on LStick
+    if jData2.axis[jAxis['LSy']] > 0 + JTHRESH:
+        mData.servos[3] += 1
+    elif jData2.axis[jAxis['LSy']] < 0 - JTHRESH:
+        mData.servos[3] -= 1
+
+    # Cage lock on RStick
+    if jData2.axis[jAxis['RSy']] > 0 + JTHRESH:
+        mData.servos[4] -= 2
+    elif jData2.axis[jAxis['RSy']] < 0 - JTHRESH:
+        mData.servos[4] += 2
+
+
+def playMacro(macro, mData):
+    # [i](servoid, pos, timestep)
+
+    for i in range(len(macro.actions)):
+        if macro.actions[i][2] == macro.timestep:
+            mData.servos[macro.actions[i][0]] = macro.actions[i][1]
+
+    macro.timestep += 1
+
+    temp = 0
+    for i in range(len(macro.actions)):
+        if temp < macro.actions[i][2]:
+            temp = macro.actions[i][2]
+
+    for i in range(len(macro.actions)):
+        if macro.timestep > temp:
+            pygame.time.set_timer(MACRO, 0)
+            macro.timestep = 0
+            mData.macroRunning = 0
+
+def sanityCheck(mData):
+    # Make sure servos are bounded
+
+    #minValues = [22, 45, 0, 0, 50, 30]
+    #maxValues = [160, 100, 180, 180, 140, 110]
+
+    minValues = [22, 0, 0, 0, 50, 30]
+    maxValues = [160, 180, 180, 180, 140, 110]
+
+    for i in range(len(mData.servos)):
+        if mData.servos[i] > maxValues[i]:
+            mData.servos[i] = maxValues[i]
+        elif mData.servos[i] < minValues[i]:
+            mData.servos[i] = minValues[i]
+
+#def writeSerialData(ser, mData):
+#    ser.reset_output_buffer()
+#    ser.reset_input_buffer()
+#
+#    # b (speed, direction)*2 servo*6 e
+#    data = [98, mData.lmotor[0], mData.lmotor[1], mData.rmotor[0], mData.rmotor[1]]
+#    data += list(mData.servos[x] for x in range(len(mData.servos)))
+#    data.append(101)
+#
+#    #print(data)
+#    
+#    ser.write(data)
+
+def writeSockData(sock, mData):
+    # b (speed, direction)*2 servo*6 e
+    data = [98, mData.lmotor[0], mData.lmotor[1], mData.rmotor[0], mData.rmotor[1]]
+    data += list(mData.servos[x] for x in range(len(mData.servos)))
+    data.append(101)
+    data = ','.join(str(e) for e in data)
+    data = data+'\r\n'
+
+    print(data)
+    #sock.sendall(data)
+    sock.sendto(data, (HOST, PORT))
+
+def drawText(s, jData, jData2, mData, macro):
+    font = pygame.font.SysFont("DejaVu Sans Mono", 18)
+
+    color = (255, 255, 255)
+    bgcolor = (0, 0, 0)
+
+    for i in range(len(jData.buttons)):
+        string = str(jData.buttons[i])
+        text = font.render(string, True, color)
+        s.blit(text, (50, 200 + 20 * i))
+
+    for i in range(len(jData.axis)):
+        string = jData.axis[i]
+        text = font.render("{: 03.2f}".format(string), True, color)
+        s.blit(text, (50, 50 + 20 * i))
+
+    string = str(jData.hat)
+    text = font.render(string, True, color)
+    s.blit(text, (50, 460))
+
+    for i in range(len(jData2.buttons)):
+        string = str(jData2.buttons[i])
+        text = font.render(string, True, color)
+        s.blit(text, (150, 200 + 20 * i))
+
+    for i in range(len(jData2.axis)):
+        string = jData2.axis[i]
+        text = font.render("{: 03.2f}".format(string), True, color)
+        s.blit(text, (150, 50 + 20 * i))
+
+    string = str(jData2.hat)
+    text = font.render(string, True, color)
+    s.blit(text, (150, 460))
+
+    for i in range(len(mData.servos)):
+        string = mData.servos[i]
+        text = font.render("{:03.2f}".format(string), True, color)
+        s.blit(text, (250, 50 + 20 * i))
+
+    string = "M: " + str(mData.macro) + " T: " + str(macro[mData.macro].timestep)
+    text = font.render(string, True, color)
+    s.blit(text, (250, 200))
+
+    string = "Data loss: " + str(mData.dataLoss)
+    text = font.render(string, True, color)
+    s.blit(text, (50, 540))
+
+def drawMeters(s, jData, jData2, mData):
+    color = (255, 255, 255)
+
+    if (jData.axis[jAxis['LSy']] > 0 + JTHRESH
+        or jData.axis[jAxis['LSy']] < 0 - JTHRESH):
+
+        lscolor = (255, 0, 0)
+    else:
+        lscolor = (0, 255, 0)
+
+    if (jData.axis[jAxis['RSy']] > 0 + JTHRESH
+        or jData.axis[jAxis['RSy']] < 0 - JTHRESH):
+
+        rscolor = (255, 0, 0)
+    else:
+        rscolor = (0, 255, 0)
+
+    rh = 500
+    rw = 150
+    rgap = 200
+
+    rx = WIDTH - (2 * rw + 2 * (rgap - rw))
+    ry = (HEIGHT - rh)/2
+
+    for i in range(0, 2):
+        pos = (rx + i * rgap, ry, rw, rh)
+        pygame.draw.rect(s, color, pos, 1)
+
+    if jData.axis[jAxis['LSy']] != 0:
+        pos = (rx + 1, ry + rh/2 + 1, rw - 2, (rh/2) * jData.axis[jAxis['LSy']] - 1)
+        pygame.draw.rect(s, lscolor, pos, 0)
+
+    if jData.axis[jAxis['RSy']] != 0:
+        pos = (rx + 1 + rgap, ry + rh/2 + 1, rw - 2, (rh/2) * jData.axis[jAxis['RSy']])
+        pygame.draw.rect(s, rscolor, pos, 0)
+
+def main():
+    # Initialize PyGame
+    pygame.init()
+    s = pygame.display.set_mode((WIDTH, HEIGHT))
+    pygame.display.set_caption("VOLVO")
+    clock = pygame.time.Clock()
+
+    # Initialize Joystick(s)
+    j = pygame.joystick.Joystick(0)
+    j.init()
+
+    #j2 = pygame.joystick.Joystick(1)
+    #j2.init()
+
+    # Timer for macros
+    clock = pygame.time.Clock()
+
+    # Data
+    jData = JoystickData()
+    jData2 = JoystickData()
+    mData = MotorData()
+
+    for i in range(len(mData.servos)):
+        mData.servos[i] = 90
+
+#    # Initialize Serial
+#    if LIVE:
+#        ser = serial.Serial()
+#        ser.port = PORT
+#        ser.baudrate = BAUDRATE
+#        ser.timeout = TIMEOUT
+#        ser.write_timeout = 1
+#        
+#        ser.open()
+
+    # Initialize Socket
+    if LIVE:
+        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+	#sock.connect((HOST, PORT))
+	#sock.sendall('Hello, permoPi! Regards Sender.')
+
+
+    # Define macros
+    # 0: Arm
+    # 1: Claw
+    # 2: Bottom
+    # 3: Top
+    # 4: Cage
+    # 5: Drop
+    macros = []
+    macros.append(Macro(1, "load", [(0, 150, 5),
+                                    (2, 110, 0),
+                                    (3, 110, 0),
+                                    (4, 50, 0),
+                                    (5, 30, 0),
+                                    (1, 45, 15),
+                                    (4, 140, 25),
+                                    (0, 90, 15),
+                                    (3, 90, 20),
+                                    (3, 110, 30)]))
+
+    macros.append(Macro(2, "drop",  [(2, 170, 0),
+                                     (3, 82, 5),
+                                     (5, 110, 10),
+                                     (4, 50, 15)]))
+
+    macros.append(Macro(3, "default", [(0, 45, 0),
+                                       (1, 90, 0),
+                                       (2, 90, 0),
+                                       (3, 90, 0),
+                                       (4, 50, 0),
+                                       (5, 30, 0)]))
+
+    # macros.append(Macro(1, "transport",
+    #                 [(0, 118, 0),
+    #                  (1, 90, 0),
+    #                  (2, 8, 0),
+    #                  (3, 0, 0),
+    #                  (4, 142, 0),
+    #                  (5, 28, 0)]
+    #                 ))
+
+    # macros.append(Macro(2, "drop",
+    #                 [(2, 170, 0),
+    #                  (3, 76, 0),
+    #                  (5, 112, 5),
+    #                  (4, 48, 7)]
+    #                 ))
+
+    # macros.append(Macro(3, "postdrop",
+    #                 [(2, 120, 0),
+    #                  (3, 26, 0),
+    #                  (3, 90, 5)]
+    #                 ))
+
+    # Main program loop
+    run = True
+    while run:
+        # Clear screen
+        s.fill(0)
+
+        # Check for keyboard and joystick events
+        for e in pygame.event.get():
+            if (e.type == pygame.QUIT or
+                    e.type == pygame.KEYDOWN and
+                    e.key == pygame.K_ESCAPE):
+                run = False
+
+            if (e.type == pygame.JOYAXISMOTION or
+                    e.type == pygame.JOYBUTTONDOWN or
+                    e.type == pygame.JOYBUTTONUP or
+                    e.type == pygame.JOYHATMOTION):
+                handleJoyEvent(e, jData, jData2)
+
+            if e.type == MACRO:
+                playMacro(macros[mData.macro], mData)
+
+        # Set mData based on jData
+        processInput(jData, jData2, mData, macros)
+
+        # Make sure data is bounded
+        sanityCheck(mData)
+
+        # Write data to serial
+        if LIVE:
+            writeSockData(sock, mData)
+
+            # print("Reading")
+            #if ser.readline():
+            #    print("OK")
+            #else:
+            #    print("DATA LOSS")
+            #    mData.dataLoss += 1
+
+        drawText(s, jData, jData2, mData, macros)
+        drawMeters(s, jData, jData2, mData)
+
+        pygame.display.flip()
+        clock.tick(FRAMERATE)
+
+    if LIVE:
+	sock.close() #TODO ADD CHECK LIKE BELOW
+        #if(ser.isOpen()):
+        #    ser.close()
+
+if __name__ == "__main__":
+    main()
diff --git a/controller/sender_v2.py b/controller/sender_v2.py
new file mode 100755
index 0000000000000000000000000000000000000000..3c596ee23fe29e59b62de814325ecf5fa0934e5e
--- /dev/null
+++ b/controller/sender_v2.py
@@ -0,0 +1,522 @@
+#!/usr/bin/python
+
+import serial
+import struct
+import pygame
+import math
+import socket
+
+# IP setup
+
+HOST = '10.19.0.2'    # The remote host
+PORT = 50007              # The same port as used by the server
+
+
+#################################################
+#   BEGIN SETTINGS
+#################################################
+
+# Pygame
+WIDTH = 800
+HEIGHT = 600
+FRAMERATE = 10
+
+# Serial
+#PORT = '/dev/ttyUSB0'
+#PORT = '/dev/ttyACM0'
+#BAUDRATE = 57600
+#TIMEOUT = 0.2
+LIVE = True
+
+# Joystick
+JTHRESH = 0.1
+
+# Wat
+MACRO = pygame.USEREVENT + 1
+
+#################################################
+#   END SETTINGS
+#################################################
+
+# Joystick data
+class JoystickData:
+    numJoys = 0
+    macro = 0
+
+    def __init__(self):
+        self.buttons = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+        self.axis = [0.0, 0.0, -1.0, 0.0, 0.0, -1.0]
+        self.hat = (0, 0)
+        JoystickData.numJoys += 1
+
+# Motor data / Serial Data
+class MotorData:
+    numMotors = 0
+    macro = 0
+    macroRunning = 0
+    dataLoss = 0
+
+    def __init__(self):
+        self.lmotor = (0, 0)
+        self.rmotor = (0, 0)
+        self.servos = [0, 0, 0, 0, 0, 0]
+        MotorData.numMotors += 1
+
+class Macro:
+    numMacros = 0
+    timestep = 0
+
+    def __init__(self, macroid, name, actions):
+        self.macroid = macroid
+        self.name = name
+        self.actions = actions
+        Macro.numMacros += 1
+
+
+# data = bytearray(b'\x00' * (2 + 4 + 10))
+
+# Joystick Dictionary
+jBtnName = ['A', 'B', 'X', 'Y', 'LB', 'RB', 'Select', 'Start', 'Logo', 'LS', 'RS']
+jBtn = {jBtnName[x]: x for x in range(len(jBtnName))}
+
+jAxisName = ['LSx', 'LSy', 'LT', 'RSx', 'RSy', 'RT']
+jAxis = {jAxisName[x]: x for x in range(len(jAxisName))}
+
+# Sets jData based on detected events
+def handleJoyEvent(e, jData, jData2):
+    if e.joy == 0:
+        if e.type == pygame.JOYAXISMOTION:
+            jData.axis[e.axis] = e.value
+
+        if e.type == pygame.JOYBUTTONDOWN:
+            jData.buttons[e.button] = 1
+
+        if e.type == pygame.JOYBUTTONUP:
+            jData.buttons[e.button] = 0
+
+        if e.type == pygame.JOYHATMOTION:
+            jData.hat = e.value
+
+    if e.joy == 1:
+        if e.type == pygame.JOYAXISMOTION:
+            jData2.axis[e.axis] = e.value
+
+        if e.type == pygame.JOYBUTTONDOWN:
+            jData2.buttons[e.button] = 1
+
+        if e.type == pygame.JOYBUTTONUP:
+            jData2.buttons[e.button] = 0
+
+        if e.type == pygame.JOYHATMOTION:
+            jData2.hat = e.value
+
+    else:
+        pass
+
+# Sets mData based on jData
+def processInput(jData, jData2, mData, macros):
+    sfactor = 235 #100
+    
+    # arm, claw, bottom, top, cagelock, droplock
+
+    #---------------------
+    #   JOYSTICK 0
+    #---------------------
+
+    # Hat
+    # if jData.hat[1] == 1:
+    #     mData.servos[3] += 2
+    # elif jData.hat[1] == -1:
+    #     mData.servos[3] -= 2
+    # elif jData.hat[0] == 1:
+    #     mData.servos[2] += 2
+    #     mData.servos[3] += 2
+    # elif jData.hat[0] == -1:
+    #     mData.servos[2] -= 2
+    #     mData.servos[3] -= 2
+
+    # Joystick buttons
+    if jData.buttons[jBtn['A']] == 1:
+        mData.servos[1] = 100
+    elif jData.buttons[jBtn['B']] == 1:
+        mData.servos[1] = 45
+
+    # Joystick triggers
+    if jData.axis[jAxis['LT']] > -1 + JTHRESH:
+        mData.servos[0] += 2
+    elif jData.axis[jAxis['RT']] > -1 + JTHRESH:
+        mData.servos[0] -= 2
+
+    # Prevent multiple macros
+    if mData.macroRunning == 0:
+        # Macros
+        if jData.buttons[jBtn['Y']] == 1:
+            mData.macro = 0
+            mData.macroRunning = 1
+            playMacro(macros[mData.macro], mData)
+            pygame.time.set_timer(MACRO, 100)
+        elif jData.buttons[jBtn['Start']] == 1:
+            mData.macro = 1
+            mData.macroRunning = 1
+            playMacro(macros[mData.macro], mData)
+            pygame.time.set_timer(MACRO, 100)
+        elif jData.buttons[jBtn['Select']] == 1:
+            mData.macro = 2
+            mData.macroRunning = 1
+            playMacro(macros[mData.macro], mData)
+            pygame.time.set_timer(MACRO, 100)
+
+    # Turbo mode
+    if jData.buttons[jBtn['RB']] == 1:
+        offset = 255 - sfactor
+    else:
+        offset = 10
+
+    # R/L stick
+    if jData.axis[jAxis['LSy']] > 0 + JTHRESH:
+        mData.lmotor = (int(math.floor(sfactor * jData.axis[jAxis['LSy']]) + offset), 0)
+    elif jData.axis[jAxis['LSy']] < 0 - JTHRESH:
+        mData.lmotor = (int(-math.floor(sfactor * jData.axis[jAxis['LSy']]) + offset - 1), 1)
+    else:
+        mData.lmotor = (0, 0)
+
+    if jData.axis[jAxis['RSy']] > 0 + JTHRESH:
+        mData.rmotor = (int(math.floor(sfactor * jData.axis[jAxis['RSy']]) + offset), 0)
+    elif jData.axis[jAxis['RSy']] < 0 - JTHRESH:
+        mData.rmotor = (int(-math.floor(sfactor * jData.axis[jAxis['RSy']]) + offset - 1), 1)
+    else:
+        mData.rmotor = (0, 0)
+
+    #---------------------
+    #   JOYSTICK 1
+    #---------------------
+
+    # Arm on LT and RT
+    if jData2.axis[jAxis['LT']] > -1 + JTHRESH:
+        mData.servos[0] += 2
+    elif jData2.axis[jAxis['RT']] > -1 + JTHRESH:
+        mData.servos[0] -= 2
+
+    # Claw on A and B
+    if jData2.buttons[jBtn['A']] == 1:
+        mData.servos[1] += 2
+    elif jData2.buttons[jBtn['B']] == 1:
+        mData.servos[1] -= 2
+
+    # Drop lock on X and Y
+    if jData2.buttons[jBtn['X']] == 1:
+        mData.servos[5] -= 2
+    elif jData2.buttons[jBtn['Y']] == 1:
+        mData.servos[5] += 2
+
+    # Cage control on HAT
+    if jData2.hat[1] == 1:
+        mData.servos[2] += 2
+        mData.servos[3] += 2
+    elif jData2.hat[1] == -1:
+        mData.servos[2] -= 2
+        mData.servos[3] -= 2
+
+    # Cage control on LStick
+    if jData2.axis[jAxis['LSy']] > 0 + JTHRESH:
+        mData.servos[3] += 1
+    elif jData2.axis[jAxis['LSy']] < 0 - JTHRESH:
+        mData.servos[3] -= 1
+
+    # Cage lock on RStick
+    if jData2.axis[jAxis['RSy']] > 0 + JTHRESH:
+        mData.servos[4] -= 2
+    elif jData2.axis[jAxis['RSy']] < 0 - JTHRESH:
+        mData.servos[4] += 2
+
+
+def playMacro(macro, mData):
+    # [i](servoid, pos, timestep)
+
+    for i in range(len(macro.actions)):
+        if macro.actions[i][2] == macro.timestep:
+            mData.servos[macro.actions[i][0]] = macro.actions[i][1]
+
+    macro.timestep += 1
+
+    temp = 0
+    for i in range(len(macro.actions)):
+        if temp < macro.actions[i][2]:
+            temp = macro.actions[i][2]
+
+    for i in range(len(macro.actions)):
+        if macro.timestep > temp:
+            pygame.time.set_timer(MACRO, 0)
+            macro.timestep = 0
+            mData.macroRunning = 0
+
+def sanityCheck(mData):
+    # Make sure servos are bounded
+
+    #minValues = [22, 45, 0, 0, 50, 30]
+    #maxValues = [160, 100, 180, 180, 140, 110]
+
+    minValues = [22, 0, 0, 0, 50, 30]
+    maxValues = [160, 180, 180, 180, 140, 110]
+
+    for i in range(len(mData.servos)):
+        if mData.servos[i] > maxValues[i]:
+            mData.servos[i] = maxValues[i]
+        elif mData.servos[i] < minValues[i]:
+            mData.servos[i] = minValues[i]
+
+#def writeSerialData(ser, mData):
+#    ser.reset_output_buffer()
+#    ser.reset_input_buffer()
+#
+#    # b (speed, direction)*2 servo*6 e
+#    data = [98, mData.lmotor[0], mData.lmotor[1], mData.rmotor[0], mData.rmotor[1]]
+#    data += list(mData.servos[x] for x in range(len(mData.servos)))
+#    data.append(101)
+#
+#    #print(data)
+#    
+#    ser.write(data)
+
+def writeSockData(sock, mData):
+    # b (speed, direction)*2 servo*6 e
+    data = [98, mData.lmotor[0], mData.lmotor[1], mData.rmotor[0], mData.rmotor[1]]
+    data += list(mData.servos[x] for x in range(len(mData.servos)))
+    data.append(101)
+    data = ','.join(str(e) for e in data)
+    data = data+'\r\n'
+
+    print(data)
+    sock.sendall(data)
+
+
+def drawText(s, jData, jData2, mData, macro):
+    font = pygame.font.SysFont("DejaVu Sans Mono", 18)
+
+    color = (255, 255, 255)
+    bgcolor = (0, 0, 0)
+
+    for i in range(len(jData.buttons)):
+        string = str(jData.buttons[i])
+        text = font.render(string, True, color)
+        s.blit(text, (50, 200 + 20 * i))
+
+    for i in range(len(jData.axis)):
+        string = jData.axis[i]
+        text = font.render("{: 03.2f}".format(string), True, color)
+        s.blit(text, (50, 50 + 20 * i))
+
+    string = str(jData.hat)
+    text = font.render(string, True, color)
+    s.blit(text, (50, 460))
+
+    for i in range(len(jData2.buttons)):
+        string = str(jData2.buttons[i])
+        text = font.render(string, True, color)
+        s.blit(text, (150, 200 + 20 * i))
+
+    for i in range(len(jData2.axis)):
+        string = jData2.axis[i]
+        text = font.render("{: 03.2f}".format(string), True, color)
+        s.blit(text, (150, 50 + 20 * i))
+
+    string = str(jData2.hat)
+    text = font.render(string, True, color)
+    s.blit(text, (150, 460))
+
+    for i in range(len(mData.servos)):
+        string = mData.servos[i]
+        text = font.render("{:03.2f}".format(string), True, color)
+        s.blit(text, (250, 50 + 20 * i))
+
+    string = "M: " + str(mData.macro) + " T: " + str(macro[mData.macro].timestep)
+    text = font.render(string, True, color)
+    s.blit(text, (250, 200))
+
+    string = "Data loss: " + str(mData.dataLoss)
+    text = font.render(string, True, color)
+    s.blit(text, (50, 540))
+
+def drawMeters(s, jData, jData2, mData):
+    color = (255, 255, 255)
+
+    if (jData.axis[jAxis['LSy']] > 0 + JTHRESH
+        or jData.axis[jAxis['LSy']] < 0 - JTHRESH):
+
+        lscolor = (255, 0, 0)
+    else:
+        lscolor = (0, 255, 0)
+
+    if (jData.axis[jAxis['RSy']] > 0 + JTHRESH
+        or jData.axis[jAxis['RSy']] < 0 - JTHRESH):
+
+        rscolor = (255, 0, 0)
+    else:
+        rscolor = (0, 255, 0)
+
+    rh = 500
+    rw = 150
+    rgap = 200
+
+    rx = WIDTH - (2 * rw + 2 * (rgap - rw))
+    ry = (HEIGHT - rh)/2
+
+    for i in range(0, 2):
+        pos = (rx + i * rgap, ry, rw, rh)
+        pygame.draw.rect(s, color, pos, 1)
+
+    if jData.axis[jAxis['LSy']] != 0:
+        pos = (rx + 1, ry + rh/2 + 1, rw - 2, (rh/2) * jData.axis[jAxis['LSy']] - 1)
+        pygame.draw.rect(s, lscolor, pos, 0)
+
+    if jData.axis[jAxis['RSy']] != 0:
+        pos = (rx + 1 + rgap, ry + rh/2 + 1, rw - 2, (rh/2) * jData.axis[jAxis['RSy']])
+        pygame.draw.rect(s, rscolor, pos, 0)
+
+def main():
+    # Initialize PyGame
+    pygame.init()
+    s = pygame.display.set_mode((WIDTH, HEIGHT))
+    pygame.display.set_caption("VOLVO")
+    clock = pygame.time.Clock()
+
+    # Initialize Joystick(s)
+    j = pygame.joystick.Joystick(0)
+    j.init()
+
+    #j2 = pygame.joystick.Joystick(1)
+    #j2.init()
+
+    # Timer for macros
+    clock = pygame.time.Clock()
+
+    # Data
+    jData = JoystickData()
+    jData2 = JoystickData()
+    mData = MotorData()
+
+    for i in range(len(mData.servos)):
+        mData.servos[i] = 90
+
+#    # Initialize Serial
+#    if LIVE:
+#        ser = serial.Serial()
+#        ser.port = PORT
+#        ser.baudrate = BAUDRATE
+#        ser.timeout = TIMEOUT
+#        ser.write_timeout = 1
+#        
+#        ser.open()
+
+    # Initialize Socket
+    if LIVE:
+        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+	sock.connect((HOST, PORT))
+	#sock.sendall('Hello, permoPi! Regards Sender.')
+
+
+    # Define macros
+    # 0: Arm
+    # 1: Claw
+    # 2: Bottom
+    # 3: Top
+    # 4: Cage
+    # 5: Drop
+    macros = []
+    macros.append(Macro(1, "load", [(0, 150, 5),
+                                    (2, 110, 0),
+                                    (3, 110, 0),
+                                    (4, 50, 0),
+                                    (5, 30, 0),
+                                    (1, 45, 15),
+                                    (4, 140, 25),
+                                    (0, 90, 15),
+                                    (3, 90, 20),
+                                    (3, 110, 30)]))
+
+    macros.append(Macro(2, "drop",  [(2, 170, 0),
+                                     (3, 82, 5),
+                                     (5, 110, 10),
+                                     (4, 50, 15)]))
+
+    macros.append(Macro(3, "default", [(0, 45, 0),
+                                       (1, 90, 0),
+                                       (2, 90, 0),
+                                       (3, 90, 0),
+                                       (4, 50, 0),
+                                       (5, 30, 0)]))
+
+    # macros.append(Macro(1, "transport",
+    #                 [(0, 118, 0),
+    #                  (1, 90, 0),
+    #                  (2, 8, 0),
+    #                  (3, 0, 0),
+    #                  (4, 142, 0),
+    #                  (5, 28, 0)]
+    #                 ))
+
+    # macros.append(Macro(2, "drop",
+    #                 [(2, 170, 0),
+    #                  (3, 76, 0),
+    #                  (5, 112, 5),
+    #                  (4, 48, 7)]
+    #                 ))
+
+    # macros.append(Macro(3, "postdrop",
+    #                 [(2, 120, 0),
+    #                  (3, 26, 0),
+    #                  (3, 90, 5)]
+    #                 ))
+
+    # Main program loop
+    run = True
+    while run:
+        # Clear screen
+        s.fill(0)
+
+        # Check for keyboard and joystick events
+        for e in pygame.event.get():
+            if (e.type == pygame.QUIT or
+                    e.type == pygame.KEYDOWN and
+                    e.key == pygame.K_ESCAPE):
+                run = False
+
+            if (e.type == pygame.JOYAXISMOTION or
+                    e.type == pygame.JOYBUTTONDOWN or
+                    e.type == pygame.JOYBUTTONUP or
+                    e.type == pygame.JOYHATMOTION):
+                handleJoyEvent(e, jData, jData2)
+
+            if e.type == MACRO:
+                playMacro(macros[mData.macro], mData)
+
+        # Set mData based on jData
+        processInput(jData, jData2, mData, macros)
+
+        # Make sure data is bounded
+        sanityCheck(mData)
+
+        # Write data to serial
+        if LIVE:
+            writeSockData(sock, mData)
+
+            # print("Reading")
+            #if ser.readline():
+            #    print("OK")
+            #else:
+            #    print("DATA LOSS")
+            #    mData.dataLoss += 1
+
+        drawText(s, jData, jData2, mData, macros)
+        drawMeters(s, jData, jData2, mData)
+
+        pygame.display.flip()
+        clock.tick(FRAMERATE)
+
+    if LIVE:
+	sock.close() #TODO ADD CHECK LIKE BELOW
+        #if(ser.isOpen()):
+        #    ser.close()
+
+if __name__ == "__main__":
+    main()